编辑:我看过答案代码:无他们做我想做的事(我已经查过)。似乎没有办法在本地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
}
他们是更好的方法吗?
对于未捕获的异常,解决方案必须与调试器下的行为完全相同。它必须导致唯一的第一次机会异常,并且调试器在异常最初抛出的时刻断开,而不是在catch块中。
具体来说,我需要在未捕获的异常上使用调试器来阻止MightThrow。
以下操作不起作用,因为它无法在正确的位置调试器中断
try { ... } catch { throw; }
这不起作用,因为它丢失了堆栈信息(并且还在错误的地方中断)。
try { ... } catch(Excption e) { throw e; }
我知道在D我可以使用scope(failure)
块
答案 0 :(得分:34)
因此,在.NET中你所要求的理论上是可行的,但这并不容易。
CIL实际上定义了五种类型的异常处理块!您在C#中习惯使用的try
,catch
和finally
以及其他两个:
filter
- 类似于catch
块,但可以运行任意代码来确定是否要处理错误,而不仅仅是匹配类型。此块可以访问异常对象,并且对异常堆栈跟踪具有与catch
块相同的效果。
fault
- 类似于finally
块,但它仅在发生异常时运行。此块无法访问异常对象,并且对异常堆栈跟踪没有影响(就像finally
块一样)。
filter
在某些.NET语言(例如VB.NET,C ++ / CLI)中可用,但遗憾的是在C#中不可用。但是,除了CIL之外,我不知道允许表达fault
块的任何语言。
因为可以在IL中完成意味着并非所有都丢失了。从理论上讲,您可以使用Reflection.Emit动态发出一个具有fault
块的函数,然后将您想要运行的代码作为lambda表达式传递(即一个用于try部分,一个用于错误部分,以及然而(a)这并不容易,而且(b)我不相信这实际上会给你一个比你现在更有用的堆栈跟踪。
对不起答案不是“这里是怎么做”类型的东西,但至少现在你知道了!你现在正在做的可能是最好的方法恕我直言。
请注意那些说问题中使用的方法是“不良做法”的人,实际上并非如此。当你实现catch
块时,你会说“当发生异常时我需要对异常对象做些什么”,当你实现finally
时,你会说“我不需要异常对象,但我需要在函数结束之前做一些事情“。
如果你真正试图说的是“我不需要异常对象,但是当发生异常时我需要做某事”那么你就在两者之间,即你想要一个{{ 1}}阻止。由于这在C#中不可用,因此您没有理想的选项,因此您也可以选择不太可能通过忘记重新抛出而导致错误的选项,并且不会破坏堆栈跟踪。
答案 1 :(得分:27)
这个怎么样:
try
{
MightThrow();
}
catch
{
DoSomethingOnFailure();
throw; // added based on new information in the original question
}
真的,这就是你所做的一切。最后是针对必须运行的事情,无论是否发生异常。
[修改:澄清]
根据您提到的注释,您希望在不修改其原始堆栈跟踪的情况下继续抛出异常。在这种情况下,你想使用我添加的朴实的投掷。这将允许异常继续向上堆栈,并仍允许您处理部分异常。典型情况可能是关闭网络连接或文件。
[第二次修改:关于您的澄清]
具体来说我需要调试器 未被捕获的异常停止在 抛出的原点(in MightThrow)不在catch区块中。
我反对打破最佳实践(是的,这是部分处理异常的最佳实践),为调试添加一些小的价值。您可以轻松检查异常以确定异常抛出的位置。
[最终修改:您有答案]
kronoz已经为您提供了所寻求的答案。不要破坏最佳实践 - 正确使用Visual Studio!您可以将Visual Studio设置为在抛出异常时完全中断。这是official info on the subject。
我实际上并不知道这个功能,所以去给他接受的答案。但请不要试图以某种时髦的方式处理异常只是为了给自己一个手动调试。你所做的就是打开更多的bug。
答案 2 :(得分:11)
如果您对调试器感兴趣,只需准确地停止发生异常的位置,那么您是否考虑过第一次机会异常?
如果打开Tools | Exceptions然后勾选Common Language Runtime Exceptions框,调试器将在异常点停止,无论是否有任何try / catch / finally块。
更新:您可以通过展开“例外”对话框中的[+]树来指定要捕获的精确异常。虽然每当指定类型[s]的任何异常发生时,它都会触发[s],你甚至可以在调试会话的中间随意打开和关闭它,所以明智的使用断点,您可以让它来进行出价。我成功地使用它来绕过'调用的目标已抛出异常'来自使用反射来实例化对象的球疼。在这种情况下非常有用的工具。另请注意,据我所知,本地人和堆栈跟踪应该牢固可用(只是进行了快速测试并且他们 可用),所以没有问题。
当然,如果你想记录那些超出IDE调试器范围的东西;在这种情况下,第一次机会异常对你没有帮助!
至少放手一搏;我发现它们非常有用,它们可能比你想象的更适合你的问题。
答案 3 :(得分:7)
出了什么问题:
try
{
MightThrow();
}
catch
{
DoSomthingOnFailure();
throw;
}
答案 4 :(得分:7)
对于只应在异常上运行的代码,请使用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();
}
答案 5 :(得分:4)
这就是你想要的。它只会在发生错误时调用此方法,并且“throw”语句将在callstack完整的情况下重新抛出异常。
try
{
MightThrow();
}
catch
{
DoSomthingOnFailure();
throw;
}
答案 6 :(得分:3)
仅在失败时运行的“finally”块称为“catch”(没有参数)。 : - )
现在,有一个小警告。如果你想为特定的异常类型设置一个专门的“catch”案例和有一个适用于所有异常的通用“catch”,你将需要做一些自定义逻辑。 / p>
因此,我会做类似的事情:
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();
}
答案 7 :(得分:2)
到目前为止,每个例子都根据我的测试丢失了原来的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;
}
答案 8 :(得分:2)
如果只捕获“MightThrow”不投掷的异常怎么样?
Bool bad = true;
try
{
MightThrow();
bad = false;
}
catch (SomePrivateMadeUpException foo)
{
//empty
}
finally
{
if(bad) DoSomeLoggingOnFailure();
}
答案 9 :(得分:2)
让我以我理解他们的方式回顾你的要求:
为了满足您的第一个要求,您应该按照每个人的建议编写代码 - 使用无参数catch和throw。
为了在使用无参数catch时满足您的第二个要求,您可以将调试器配置为在抛出异常时中断,而不仅仅是在存在未处理的异常时。我怀疑你知道怎么做,但我会把它放在这里以回答完整性:在VS中你可以在Debug中做到这一点 - >例外 - >公共语言运行时异常 - >选中Thrown复选框。
如果你知道你的应用程序会抛出很多处理过的异常,那么这可能不是你的选择。此时,您唯一的选择是满足您的第一个要求是编写代码以最终用于异常记录目的,或者查看直接IL发射路径,如Greg Beech建议的那样。
但是,最终代码是否正在执行取决于您使用的调试器。特别是,VS会在最终执行之前中断未处理的异常,并且不会让你继续。因此,除非您在此时从进程中分离,否则将永远不会执行您的日志记录代码。换句话说,第二个要求将妨碍满足第一个要求。
答案 10 :(得分:2)
您可以将逻辑封装在自定义类中,例如:
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();
}
}
}
}
并将其用作:
new Executor(MightThrow, DoSomeLoggingOnFailure).Run();
希望这有帮助。
答案 11 :(得分:1)
这不是:
try
{
MightThrow();
}
catch (Exception e)
{
DoSomethingOnFailure();
throw e;
}
答案 12 :(得分:1)
你可以写一下,或者有人为你写一个VB.net中的小程序集,它实现了一个接受四个委托的TryFaultCatchFinally(of T)方法:
请注意,当执行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
答案 13 :(得分:0)
不,我认为这是你习惯的常用习语。
EDIT 要清楚,“catch”然后“rethrow”策略提供相同的运行时语义,但是当连接VS调试器时它们会改变体验。工具和维护很重要;调试通常需要你“捕获所有第一次机会异常”,如果由于代码中的catch-then-rethrow而导致大量“虚假”的第一次机会异常,那么它实际上会损害调试代码的能力。这个成语是关于与工具的良好交互,以及清楚地表达意图(你不想'捕捉',决定无法处理,并重新抛出,而你只是想记录异常确实发生但是让传递给)。
答案 14 :(得分:0)
您是否考虑过使用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;
}
}
答案 15 :(得分:0)
在C#6中添加了异常过滤器后,一种选择是利用错误的返回异常过滤器,如下所示:
react-native link
有关异常过滤器的更多详细信息,请参见https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch。
答案 16 :(得分:-1)
try
{
MightThrow();
}
catch
{
DoSomethingOnFailure();
}