Элегантный способ очистки нескольких ресурсов при возникновении исключений - PullRequest
1 голос
/ 14 сентября 2011

Я работаю в некотором коде, который часто использует исключения для обработки определенных состояний ошибки (конечно, это не самый лучший дизайн, но это то, с чем я работаю).

Когда возникает исключениев коде мне нужен элегантный способ очистки любых открытых или временных ресурсов.

Это можно выполнить следующим образом:

try
{
    foo();
    bar();
}
catch (Exception)
{
    // Oops, an error occurred - let's clean up resources
    // Any attempt to cleanup non-existent resources will throw
    // an exception, so let's wrap this in another try block
    try
    {
        cleanupResourceFoo();
        cleanupResourceBar();
    }
    catch
    {
        // A resource didn't exist - this is non-fatal so let's drop
        // this exception
    }
}

Допустим, что метод foo() очищен послесам по себе, но метод bar() вызвал исключение.В коде очистки мы будем вызывать cleanupResourceFoo() first , что само вызовет исключение, поскольку ресурсы foo уже очищены.

Это означает, что cleanupResourceBar() выиграно 'В итоге вызов будет вызван утечкой ресурсов.

Конечно, мы можем переписать внутренний блок try/catch следующим образом:

try
{
    cleanupResourceFoo();
}
catch
{
}
try
{
    cleanupResourceBar();
}
catch
{
}

, но теперь мыЯ становлюсь довольно уродливым.

Я пришел из C ++, и для таких вещей я бы обычно использовал RAII.Любые рекомендации по поводу элегантного способа справиться с этим в C #?

1 Ответ

6 голосов
/ 14 сентября 2011

Очистка ресурсов почти всегда должна осуществляться с помощью using операторов и IDisposable - так что вы просто должны иметь:

using (FirstResource r1 = ...)
{
    using (SecondResource r2 = ...)
    {
        ...
    }
}

Если вы только хотите очиститьресурсы в порядке исключения, это немного реже - и я бы не ожидал, что RAII особенно поможет вам в C ++.Вы могли бы потенциально использовать делегатов, чтобы сделать это проще:

TryWithCleanUpOnException(foo, cleanUpResourceFoo);
TryWithCleanUpOnException(bar, cleanUpResourceBar);

...

private static void TryWithCleanUpOnException(Action action,
                                              Action cleanUp)
{
    bool success = false;
    try
    {
        action();
        success = true;
    }
    finally
    {
        if (!success)
        {
            cleanup();
        }
    }
}

Не перехват исключения, это позволяет ошибкам распространяться, а не проглатываться.Это обычно , что вы хотите - если это не так, возможно, вы могли бы объяснить более точно, какова ваша ситуация.

Вы сказали, что есть нефатальные исключениячто вы фактически хотите игнорировать - но обычно вы не должны просто ловить Exception и продолжать: поймать определенные исключения, которые вы ожидаете в определенных ситуациях.Очевидно, у вас может быть особая ситуация, но это общий совет, который держится большую часть времени.Вы можете реструктурировать вспомогательный метод, описанный выше, чтобы перехватывать исключения, если вы действительно этого хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...