C# “c”;最后";仅在异常情况下运行的块
编辑:我已经查看了答案代码:没有一个做了我想做的事(我已经检查过了)。在本机c#中似乎没有办法实现我想要的功能。我想这不是一场灾难,只是一个遗憾,因为.NET确实支持它(见公认答案) 谢谢大家C# “c”;最后";仅在异常情况下运行的块,c#,exception-handling,finally,C#,Exception Handling,Finally,编辑:我已经查看了答案代码:没有一个做了我想做的事(我已经检查过了)。在本机c#中似乎没有办法实现我想要的功能。我想这不是一场灾难,只是一个遗憾,因为.NET确实支持它(见公认答案) 谢谢大家 我有这样的c#代码(测试框架的一部分,除非在调试器下运行),谁的意思是要避免实际捕获异常,因为这会使调试堆栈中未缠绕部分的代码变得非常痛苦 Bool bad = true; try { MightThrow(); bad = false; } finally { if(bad) DoS
我有这样的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
}
他们的方法更好吗
对于未捕获的异常,解决方案的行为必须与调试器下的行为完全相同。它必须导致只有一次第一次机会的异常,并且调试器在异常最初抛出时中断,而不是在catch块中
具体地说,我需要对未捕获的异常使用调试器来停止内部MightThrow
以下操作不起作用,因为它无法使调试器在正确的位置中断
try { ... } catch { throw; }
这不起作用,因为它会丢失堆栈信息(并且会在错误的位置中断)
我知道我可以用一个block,不,我认为这是一个常见的习惯用法 编辑 需要明确的是,“catch”和“rethrow”策略提供了相同的运行时语义,但是它们在连接VS调试器时会改变体验。工具和维护很重要;调试通常要求您“捕获所有第一次机会异常”,如果由于捕获而导致大量“虚假”第一次机会异常,然后在代码中重新刷新,这确实会损害调试代码的能力。这个习惯用法是关于与工具进行良好的交互,以及清楚地表达意图(你不想“捕获”,决定不能处理,然后重试,相反,你只想记录一个异常确实发生了,但让它传递过去)。这样如何:
try
{
MightThrow();
}
catch
{
DoSomethingOnFailure();
throw; // added based on new information in the original question
}
真的,你就这么做了。最后是针对无论是否发生异常都必须运行的事情
[编辑:澄清]
根据前面提到的注释,您希望继续抛出异常,而不修改其原始堆栈跟踪。在这种情况下,您希望使用我添加的朴素投掷。这将允许异常在堆栈中继续,并且仍然允许您处理部分异常。典型情况可能是关闭网络连接或文件
[第二次编辑:关于您的澄清]
特别是我需要的调试器上
未捕获的异常停止在
投掷的起始点(向内)
(可能会扔)不在接球区
我反对打破最佳实践(是的,这是部分处理异常的最佳实践),为调试增加一些次要价值。您可以轻松地检查异常以确定异常抛出的位置
[最终编辑:你有你的答案]
深思熟虑地为你提供了你所寻求的答案。不要破坏最佳实践--正确使用Visual Studio!您可以将Visual Studio设置为在引发异常时完全中断。给你
事实上,我不知道这个特征,所以去给他一个公认的答案吧。但是,请不要试图用一些古怪的方式来处理异常,仅仅是为了给自己一个手动调试的机会。你所做的就是让自己面对更多的虫子 有什么问题吗:
try
{
MightThrow();
}
catch
{
DoSomethingOnFailure();
}
try
{
MightThrow();
}
catch
{
DoSomthingOnFailure();
throw;
}
这不就是:
try
{
MightThrow();
}
catch (Exception e)
{
DoSomethingOnFailure();
throw e;
}
?对于只应在异常情况下运行的代码,请使用catch块:
try
{
MightThrow();
}
catch (Exception ex)
{
// this runs only when there was an exception
DoSomthingOnFailure();
// pass exception on to caller
throw;
}
finally
{
// this runs everytime
Cleanup();
}
这就是你想要的。它仅在发生错误时调用此方法,“throw”语句将在callstack保持不变的情况下重新抛出异常
try
{
MightThrow();
}
catch
{
DoSomthingOnFailure();
throw;
}
仅在失败时运行的“finally”块称为“catch”(不带参数)。:-)
现在,有一个小警告。如果您想为特定的异常类型提供一个专门的“catch”案例,并为所有异常提供一个通用的“catch”,那么您必须执行一些自定义逻辑
因此,我会这样做:
try
{
MightThrow();
}
catch(MyException ex)
{
// Runs on MyException
MySpecificFailureHandler()
// Since we have handled the exception and can't execute the generic
// "catch" block below, we need to explicitly run the generic failure handler
MyGenericFailureHandler()
}
catch
{
// Runs on any exception hot handled specifically before
MyGenericFailureHandler()
// If you want to mimic "finally" behavior and propagate the exception
// up the call stack
throw;
}
finally
{
// Runs on any failure or success
MyGenericCleanupHandler();
}
public class Executor
{
private readonly Action mainActionDelegate;
private readonly Action onFaultDelegate;
public Executor(Action mainAction, Action onFault)
{
mainActionDelegate = mainAction;
onFaultDelegate = onFault;
}
public void Run()
{
bool bad = true;
try
{
mainActionDelegate();
bad = false;
}
finally
{
if(bad)
{
onFaultDelegate();
}
}
}
}
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
如果您对调试器感兴趣,只需在异常发生的地方停止,那么您是否考虑过第一次出现异常 如果打开Tools | Exceptions,然后勾选Common Language Runtime Exceptions(公共语言运行时异常)框,则调试器将在异常点停止,而不考虑任何try/catch/finally块 更新:通过展开异常对话框中的[+]树,可以指定要捕获的准确异常。当然,每当指定类型[S]发生异常时,它都会开火,即使在调试会话的中间,您也可以随意打开和关闭它,因此明智地使用断点,您可以得到它来完成您的命令。我使用它成功地绕过了“调用的目标抛出了异常”的问题,该问题源于使用反射实例化对象。在这种情况下非常有用的工具。还要注意的是,据我记忆所及,局部变量和堆栈跟踪应该是稳定可用的(只是做了一个快速测试,它们是可用的),所以没有问题 当然,如果您想记录事情,那么这就超出了IDE调试器的范围;在这种情况下,第一次机会的例外不会帮助你
至少试一试吧;我发现它们非常有用,可能比您想象的更适合您的问题。根据我的测试,到目前为止,每个示例都失去了原始的堆栈跟踪。这里有一个适合你的解决方案
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;
}
只捕获“MightThrow”不会抛出的异常如何
Bool bad = true;
try
{
MightThrow();
bad = false;
}
catch (SomePrivateMadeUpException foo)
{
//empty
}
finally
{
if(bad) DoSomeLoggingOnFailure();
}
所以,在.NET中,您所要求的在理论上是可能的,但这并不容易 CIL实际上定义了五种类型的异常处理块!
尝试
,捕获
和最后
您习惯的
new Executor(MightThrow, DoSomeLoggingOnFailure).Run();
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
[DebuggerStepThrough]
internal void MyHelper(Action someCallback)
{
try
{
someCallback();
}
catch(Exception ex)
{
// Debugger will not break here
// because of the DebuggerStepThrough attribute
DoSomething(ex);
throw;
}
}
void PerformMightThrowWithExceptionLogging()
{
try
{
MightThrow();
}
catch (Exception e) when (Log(e))
{
// Cannot enter here, since Log returns false.
}
}
bool Log(Exception e)
{
DoSomeLoggingOnFailure(e);
// Return false so the exception filter is not matched, and therefore the stack is kept.
// This means the debugger breaks where the exception actually happened, etc.
return false;
}