Определите при выполнении в наконец блоке из-за выданного исключения

Действительно ли возможно определить, выполняется ли код в настоящее время в контексте a finally обработчик в результате выданного исключения? Я довольно люблю использование IDisposable шаблон для реализации функциональности обзора записи/выхода, но одного беспокойства с этим шаблоном - то, что Вы не могли бы обязательно хотеть, чтобы поведение конца объема произошло, если исключение происходит в теле using. Я искал бы что-то вроде этого:

public static class MyClass
{
    public static void MyMethod()
    {
        using (var scope = MyScopedBehavior.Begin())
        {
            //Do stuff with scope here
        }
    }
}

public sealed class MyScopedBehavior : IDisposable
{
    private MyScopedBehavior()
    {
        //Start of scope behavior
    }

    public void Dispose()
    {
        //I only want to execute the following if we're not unwinding
        //through finally due to an exception:
        //...End of scope behavior    
    }

    public static MyScopedBehavior Begin()
    {
        return new MyScopedBehavior();
    }
}

Существуют другие способы, которыми я могу выполнить это (передайте делегата в функции, которая окружает вызов особым поведением), но мне любопытно, если возможно сделать это с помощью IDisposable шаблон.


На самом деле это, по-видимому, спросили и ответили прежде здесь. Возможно обнаружить в очень hackish вид пути. Я на самом деле не использовал бы ту технику, но интересно знать, что это возможно.

33
задан Community 23 May 2017 в 10:30
поделиться

6 ответов

Средства для выполнения этого, которые я видел, требуют дополнительного метода:

public static void MyMethod()
{
    using (var scope = MyScopedBehavior.Begin())
    {
        //Do stuff with scope here
        scope.Complete(); // Tells the scope that it's good
    }
}

Таким образом, ваш объект области может отследить, удаляется ли он из-за ошибки или успешной операции. Это подход, используемый, например, TransactionScope (см. TransactionScope.Complete ]) .

17
ответ дан 27 November 2019 в 18:27
поделиться

В качестве дополнительной точки, IL позволяет указать блоки SEH разломов , которые похожи на , наконец, , но вводятся только когда генерируется исключение - вы можете увидеть пример здесь , примерно на 2/3 вниз по странице. К сожалению, C # не предоставляет эту функциональность.

14
ответ дан 27 November 2019 в 18:27
поделиться

Лучшее, что я могу придумать, это:

using (var scope = MyScopedBehavior.Begin())
{
  try
  {
    //Do stuff with scope here
  }
  catch(Exception)
  {
    scope.Cancel();
    throw;
  }
}

Конечно, scope.Cancel () гарантирует, что ничего не происходит в Dispose ()

5
ответ дан 27 November 2019 в 18:27
поделиться

Я думаю, что лучший способ - использовать предложение write out try / catch / finally вручную. Изучите элемент из первой книги «Эффективный c #». Хороший хакер на C # должен точно знать, до чего использование расширяется. Со времен .Net 1.1 он немного изменился - теперь у вас может быть несколько, использующих один под другим. Итак, используйте отражатель и изучите несахарный код.

Затем, когда вы напишете свой собственный код - либо используйте , используя , либо напишите свой собственный материал. Это не так уж сложно и полезно знать.

Вы можете придумать другие приемы, но они кажутся слишком тяжелыми и даже неэффективными. Позвольте мне включить образец кода.

LAZY WAY :

using (SqlConnection cn = new SqlConnection(connectionString))
using (SqlCommand cm = new SqlCommand(commandString, cn))
{
    cn.Open();
    cm.ExecuteNonQuery();
}

MANUAL WAY :

bool sawMyEx = false;
SqlConnection cn =  null;
SqlCommand cm = null;

try
{
    cn = new SqlConnection(connectionString);
    cm = new SqlCommand(commandString, cn);
    cn.Open();
    cm.ExecuteNonQuery();
}
catch (MyException myEx)
{
    sawMyEx = true; // I better not tell my wife.
    // Do some stuff here maybe?
}
finally
{
    if (sawMyEx)
    {
        // Piss my pants.
    }

    if (null != cm);
    {
        cm.Dispose();
    }
    if (null != cn)
    {
        cn.Dispose();
    }
}
1
ответ дан 27 November 2019 в 18:27
поделиться

В следующем шаблоне устраняется проблема неправильного использования API, т. Е. Метод завершения области не вызывается, то есть полностью опускается или не вызывается из-за логического условия. Я думаю, что это более подробно отвечает на ваш вопрос и даже меньше кода для пользователя API.

Править

Еще более прямолинейно после комментария Дэна:

public class Bling
{
    public static void DoBling()
    {
        MyScopedBehavior.Begin(() =>
        {
            //Do something.
        }) ;
    }   
}

public static class MyScopedBehavior
{
    public static void Begin(Action action)
    {
        try
        {
            action();

            //Do additonal scoped stuff as there is no exception.
        }
        catch (Exception ex)
        {
            //Clean up...
            throw;
        }
    }
}   
4
ответ дан 27 November 2019 в 18:27
поделиться

Почему бы просто не утилизировать внутри блока try { } в самом конце, и не использовать finally вообще? Похоже, это именно то поведение, которое вы ищете.

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

0
ответ дан 27 November 2019 в 18:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: