如何确定.NET异常是否正在被处理?
我们正在研究 C# 中的一种编码模式,其中我们希望将“using”子句与一个特殊类一起使用,其 Dispose()
方法根据“using”主体是否执行不同的操作正常退出或异常退出。
据我所知,CLR 会跟踪当前正在处理的异常,直到它被“catch”处理程序消耗为止。然而,尚不完全清楚这些信息是否以任何方式暴露给代码访问。您知道是否存在,如果是,如何访问它?
例如:
using (var x = new MyObject())
{
x.DoSomething();
x.DoMoreThings();
}
class MyObject : IDisposable
{
public void Dispose()
{
if (ExceptionIsBeingHandled)
Rollback();
else
Commit();
}
}
这看起来几乎像 System.Transactions.TransactionScope ,只不过成功/失败不是由调用 x.Complete() 确定的,而是基于是否using
主体正常退出。
We're investigating a coding pattern in C# in which we'd like to use a "using" clause with a special class, whose Dispose()
method does different things depending on whether the "using" body was exited normally or with an exception.
To the best of my understanding, the CLR keeps track of the current exception being handled until it's been consumed by a "catch" handler. However it's not entirely clear whether this information is exposed in any way for the code to access. Do you know whether it is, and if so, how to access it?
For example:
using (var x = new MyObject())
{
x.DoSomething();
x.DoMoreThings();
}
class MyObject : IDisposable
{
public void Dispose()
{
if (ExceptionIsBeingHandled)
Rollback();
else
Commit();
}
}
This looks almost like System.Transactions.TransactionScope
, except that success/failure is not determined by a call to x.Complete()
, but rather based on whether the using
body was exited normally.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
https://www.codewrecks.com/post/old/2008/07/Detecting-if-finally-block-is-executing-for-an-manhandled-exception/ 描述了一种用于检测的“黑客”行为您的代码是否在异常处理模式下执行。它使用 Marshal.GetExceptionPointers 查看异常是否“活动”。
但请记住:
https://www.codewrecks.com/post/old/2008/07/detecting-if-finally-block-is-executing-for-an-manhandled-exception/ describes a "hack" to detect if your code is executed in exception handling mode or not. It uses Marshal.GetExceptionPointers to see if an exception is "active".
But keep in mind:
不是问题的答案,只是注意我从未最终在实际代码中使用“接受的”黑客,因此它仍然在很大程度上未经“野外”测试。相反,我们采用了这样的方法:
其中
关键点是它与问题中的“使用”示例一样紧凑,并且不使用任何 hack。
其中的缺点是性能损失(在我们的例子中完全可以忽略不计),以及当我实际上希望 F10 直接进入
x.DoSomething()
时,它会进入 DoThings。两者都很小。Not an answer to the question, but just a note that I never ended up using the "accepted" hack in real code, so it's still largely untested "in the wild". Instead we went for something like this:
where
The key point is that it's as compact as the "using" example in the question, and doesn't use any hacks.
Among the drawbacks is a performance penalty (completely negligible in our case), and F10 stepping into
DoThings
when I actually want it to just step straight tox.DoSomething()
. Both very minor.您无法获得此信息。
我将使用类似于 DbTransaction 类所使用的模式:也就是说,您的 IDisposable 类应该实现与 DbTransaction.Commit() 类似的方法。然后,您的 Dispose 方法可以根据是否调用 Commit 来执行不同的逻辑(在 DbTransaction 的情况下,如果未显式提交事务,则事务将回滚)。
然后,您班级的用户将使用以下模式,类似于典型的 DbTransaction:
编辑 我看到您已编辑您的问题以表明您了解此模式(您的示例使用 TransactionScope)。尽管如此,我认为这是唯一现实的解决方案。
This information isn't available to you.
I would use a pattern similar to that used by the DbTransaction class: that is, your IDisposable class should implement a method analagous to DbTransaction.Commit(). Your Dispose method can then perform different logic depending on whether or not Commit was called (in the case of DbTransaction, the transaction will be rolled back if it wasn't explicity committed).
Users of your class would then use the following pattern, similar to a typical DbTransaction:
EDIT I see you've edited your question to show you're aware of this pattern (your example uses TransactionScope). Nevertheless I think it's the only realistic solution.
using 语句只是 try finally 块的语法糖。您可以通过完整地编写 tryfinally ,然后添加一个 catch 语句来处理您的特殊情况来获得您想要的东西:
在 MyThing 中,如果您愿意,您可以这样做,例如:
注意:我还想知道为什么您想要做这个。它有一些代码味道。 Dispose 方法旨在清理非托管资源,而不是处理异常。您可能最好在 catch 块中编写异常处理代码,而不是在 dispose 中,并且如果您需要共享代码,请创建一些可以从这两个地方调用的有用的辅助函数。
这是做你想做的事情的更好方法:
A using statement is just syntactic sugar for a try finally block. You can get what you want by writing the try finally out in full and then adding a catch statement to handle your special case:
Inside MyThing you can do this if you want, for example:
Note: I also have to wonder why you want to do this. It has some code smell. The Dispose method is intended to clean up unmanaged resources, not to handle exceptions. You would probably be better off writing your exception handling code in the catch block, not in the dispose, and if you need to share code make some useful helper functions that you can call from both places.
Here's a better way of doing what you want:
这似乎并不是一个坏主意。它在 C#/.NET 中似乎并不理想。
在 C++ 中,有一个函数可以使代码检测它是否由于异常而被调用。这对于 RAII 析构函数来说是最重要的;对于析构函数来说,根据控制流是正常还是异常来选择提交或中止是一件小事。我认为这是一种相当自然的方法,但缺乏内置支持(以及解决方法在道德上可疑的性质;对我来说,它感觉相当依赖于实现)可能意味着应该采取更传统的方法。
This doesn't seem to be that bad an idea; it just doesn't seem to be ideal in C#/.NET.
In C++ there is a function that enables code to detect if it's being called due to an exception. This is of most importance in RAII destructors; it is a trivial matter for the destructor to choose to commit or abort depending on whether control flow is normal or exceptional. I think this is a fairly natural approach to take, but the lack of built-in support (and the morally dubious nature of the workaround; it feels rather implementation-dependent to me) probably means that a more conventional approach should be taken.