Действительно ли возможно определить, выполняется ли код в настоящее время в контексте 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 вид пути. Я на самом деле не использовал бы ту технику, но интересно знать, что это возможно.
Средства для выполнения этого, которые я видел, требуют дополнительного метода:
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 ]) .
В качестве дополнительной точки, IL позволяет указать блоки SEH разломов
, которые похожи на , наконец,
, но вводятся только когда генерируется исключение - вы можете увидеть пример здесь , примерно на 2/3 вниз по странице. К сожалению, C # не предоставляет эту функциональность.
Лучшее, что я могу придумать, это:
using (var scope = MyScopedBehavior.Begin())
{
try
{
//Do stuff with scope here
}
catch(Exception)
{
scope.Cancel();
throw;
}
}
Конечно, scope.Cancel ()
гарантирует, что ничего не происходит в Dispose ()
Я думаю, что лучший способ - использовать предложение 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();
}
}
В следующем шаблоне устраняется проблема неправильного использования 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;
}
}
}
Почему бы просто не утилизировать внутри блока try { }
в самом конце, и не использовать finally вообще? Похоже, это именно то поведение, которое вы ищете.
Это также кажется более реалистичным с точки зрения того, как другие могут использовать ваш класс. Вы уверены, что все, кто когда-либо будет его использовать, никогда не захотят избавиться от него в случае исключения? Или это поведение должно обрабатываться потребителем класса?