c # блок "finally", который работает только на исключениях - PullRequest
7 голосов
/ 10 февраля 2009

Редактировать: я посмотрел код ответа: НЕТ из них делают то, что я хочу (я проверял). Казалось бы, нет никакого способа сделать то, что я хочу в нативном C #. Я полагаю, что это не катастрофа, просто позор, учитывая, что .NET поддерживает это (см. Принятый ответ).

Спасибо всем.


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

Bool bad = true;
try
{
   MightThrow();
   bad = false;
}
finally
{
   if(bad) DoSomeLoggingOnFailure();

   //// Does not catch!!!! 
   //// exception continues to unwind stack.

   //// Note that re throwing the exception is NOT
   //// the same as not catching it in the first place
}

это лучший способ сделать это?

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

В частности, мне нужен отладчик для необнаруженных исключений, чтобы остановить MightThrow в стороне.

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

try { ... } catch { throw; }

И это не работает, потому что оно теряет информацию о стеке (а также разрывается в неправильном месте).

try { ... } catch(Excption e) { throw e; }

Я знал, что в D я мог бы использовать scope(failure) блок

Ответы [ 16 ]

2 голосов
/ 10 февраля 2009

Каждый пример пока проигрывает оригинальную StackTrace в соответствии с моими тестами. Вот решение, которое должно работать для вас.

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

try
{
   MightThrow();
}
catch (Exception ex)
{
    DoSomethingOnFailure();
    PreserveStackTrace(ex);
    throw;
}
1 голос
/ 07 января 2011

Вы можете написать или попросить кого-нибудь написать для вас небольшую сборку на VB.net, которая реализует метод TryFaultCatchFinally (из T), который принимает четыре делегата:

  1. TryMethod - Действие (для T) для выполнения блока «Try».
  2. FaultMethod - Предикат (Of T, Exception), который, если возникнет исключение, будет называться до того, как запустятся любые блоки finally; если он возвращает true, блок Catch запустится, иначе - нет.
  3. CatchMethod - Действие (Of T, Exception), которое должно быть выполнено, если произошло исключение и FaultMethod вернул true; происходит после запуска блоков finally.
  4. finallyMethod - Действие (OF T, Exception, Boolean), которое должно быть выполнено как блок «Наконец». Переданное исключение будет нулевым, если TryMethod будет выполнен до конца, или будет содержать исключение, которое вызвало его выход. Логическое значение будет истинным, если исключение было обнаружено, или ложным в противном случае.

Обратите внимание, что при выполнении FaultMethod можно проверить состояние объектов, вызвавших исключение, до того, как такое состояние будет уничтожено блоками finally. При этом следует проявлять осторожность (любые блокировки, которые были удержаны, когда было сгенерировано исключение, будут продолжать удерживаться), но иногда эта способность может оказаться полезной, особенно при отладке.

Я бы предложил, чтобы рутина выглядела примерно так:

    Shared Sub TryFaultCatchFinally(Of T)(ByVal TryProc As Action(Of T), _
                                          ByVal FaultProc As Func(Of T, Exception, Boolean), _
                                          ByVal CatchProc As Action(Of T, Exception), _
                                          ByVal FinallyProc As Action(Of T, Exception, Boolean), _
                                          ByVal Value As T)
        Dim theException As Exception = Nothing
        Dim exceptionCaught As Boolean = False
        Try
            TryProc(Value)
            theException = Nothing
            exceptionCaught = False
        Catch Ex As Exception When CopyExceptionAndReturnFalse(Ex, theException) OrElse FaultProc(Value, Ex)
            exceptionCaught = True
            CatchProc(Value, Ex)
        Finally
            FinallyProc(Value, theException, exceptionCaught)
        End Try
    End Sub
1 голос
/ 10 февраля 2009

Разве это не то же самое, что:

try 
{
    MightThrow();
}
catch (Exception e) 
{
    DoSomethingOnFailure();
    throw e;
}

0 голосов
/ 13 января 2011

Рассматривали ли вы использование атрибута DebuggerStepThrough? http://msdn.microsoft.com/en-us/library/system.diagnostics.debuggerstepthroughattribute.aspx

[DebuggerStepThrough]
internal void MyHelper(Action someCallback)
{
    try
    {
        someCallback();
    }
    catch(Exception ex)
    {
        // Debugger will not break here
        // because of the DebuggerStepThrough attribute
        DoSomething(ex);
        throw;
    }
}
0 голосов
/ 10 февраля 2009
try
{
   MightThrow();
}
catch
{
   DoSomethingOnFailure();
}
0 голосов
/ 10 февраля 2009

Нет, я думаю, что это обычная идиома, как у вас есть.

EDIT Для ясности, стратегии «catch» и «rethrow» предлагают ту же семантику времени выполнения, однако они меняют работу, когда подключен VS-отладчик. Оснастка и обслуживание важны; Отладка часто требует от вас «перехвата всех исключений первого шанса», и если в результате вы получите множество «ложных» исключений первого шанса из-за перехвата, а затем переброса в вашем коде, это действительно повредит возможности отладки кода. Эта идиома заключается в том, чтобы хорошо взаимодействовать с инструментарием, а также четко выражать намерение (вы не хотите «ловить», решать, что не можете обработать, и отбрасывать, вместо этого вы просто хотите регистрировать, что исключение действительно произошло, но пусть это проходит мимо).

...