.net 如何处理从Dispose引发的异常?
最近,我在研究一些关于未处理对象的棘手错误.net 如何处理从Dispose引发的异常?,.net,dispose,idisposable,.net,Dispose,Idisposable,最近,我在研究一些关于未处理对象的棘手错误 public class SomeClass: IDisposable { void Dispose() { if (m_foo != null) { m_foo.Dispose(); } if (m_bar != null) { m_bar.Dispose(); } } private F
public class SomeClass: IDisposable
{
void Dispose()
{
if (m_foo != null)
{
m_foo.Dispose();
}
if (m_bar != null)
{
m_bar.Dispose();
}
}
private Foo m_foo;
private Bar m_bar;
}
我在代码中找到了一些模式。据报道,一些mu-foo没有被释放,而SomeClass的所有实例似乎都已被释放
public class SomeClass: IDisposable
{
void Dispose()
{
if (m_foo != null)
{
m_foo.Dispose();
}
if (m_bar != null)
{
m_bar.Dispose();
}
}
private Foo m_foo;
private Bar m_bar;
}
我怀疑Foo.Dispose可能会抛出异常,所以下面的代码不会执行,所以m_bar不会被释放
因为Foo/Bar可能来自第三方,所以不能保证不会引发异常
如果只是用try-catch包装所有Dispose调用,代码将变得笨拙
处理此问题的最佳做法是什么?如果在终结上下文中调用Dispose(),并引发异常,则进程将终止
如果您怀疑Foo.Dispose()正在引发异常,我会在可能的情况下最后处理它,并将其包装在try/catch中。尽一切可能在catch中消除它-将引用设置为null。从Dispose()抛出异常是非常糟糕的,应该避免
不幸的是,如果这是有缺陷的第三方代码,那么最好的办法就是让他们修复它。之后你不应该手动清理
希望这能有所帮助。确实,泄露dispose方法的异常可能非常糟糕,特别是因为实现IDisposable的东西通常会指定一个调用dispose的终结器 问题是,通过处理异常来彻底解决问题可能会使您遇到一些非常难以调试的情况。如果您的IDisposable分配了一个只有在dispose之后才能释放的关键部分,该怎么办。如果忽略异常发生的事实,则可能会导致死锁中心。我认为Dispose中的失败应该是您希望尽早失败的情况之一,这样您可以在发现错误后尽快修复它 当然,这一切都取决于要处理的对象,对于某些对象,您可能能够恢复,而其他对象则无法恢复。一般的经验法则是,正确使用Dispose时不应抛出异常,也不应在调用的嵌套Dispose方法中对异常进行防御性编码 你真的不想把OutOfMemoryException掩盖起来吗
如果我有一个狡猾的第三方组件,它任意地在Dispose上抛出异常,我会将它修复,并将它托管在一个单独的进程中,当它开始播放时,我可以将其拆除 因为不必在using()语句中分配变量,为什么不为此使用“stacked”using语句呢
void Dispose()
{
// the example in the question didn't use the full protected Dispose(bool) pattern
// but most code should have if (!disposed) { if (disposing) { ...
using (m_foo)
using (m_bar)
{
// no work, using statements will check null
// and call Dispose() on each object
}
m_bar = null;
m_foo = null;
}
“stacked”using语句展开如下:
using (m_foo)
{
using (m_bar) { /* do nothing but call Dispose */ }
}
因此,Dispose()调用放在单独的finally块中:
try {
try { // do nothing but call Dispose
}
finally {
if (m_bar != null)
m_bar.Dispose();
}
finally {
if (m_foo != null)
m_foo.Dispose();
}
我很难在一个地方找到这方面的参考资料。“堆叠”使用语句在一个(见“C”段和VB中使用语句、C++堆栈语义)中找到。IDisposable上的许多StackOverflow答案都引用了Joe Duffy帖子。我还发现,使用局部变量语句进行堆栈似乎很常见。除了(C#3.0规范中的第8.13节),我在任何地方都找不到finally块的链接,并且只针对单个“using”块中的多个变量,这与我的建议不完全一致,但是如果你分解IL,你会发现try/finally块是嵌套的。在null检查中,同样来自C#spec:“如果获取了null资源,则不会调用Dispose,也不会引发异常。”为了避免重复处理对象的代码,我编写了以下静态方法
public static void DisposeObject<T>(ref T objectToDispose) where T : class
{
IDisposable disposable = objectToDispose as IDisposable;
if (disposable == null) return;
disposable.Dispose();
objectToDispose = null;
}
公共静态void dispose对象(ref T objectToDispose),其中T:class
{
IDisposable disposable=对象显示为IDisposable;
如果(一次性==null)返回;
一次性的,一次性的;
objectToDispose=null;
}
主要的一点是,您可以使它成为一个函数,因此您只需为每个要处理的对象键入一行,以保持处理方法的整洁。在我们的例子中,默认为空处理指针,因此使用ref参数
在您的情况下,您可能希望添加异常处理,或者使用异常处理创建不同的风格。我会确保每当Dispose()抛出异常时都记录/断点,但如果您无法阻止它,下一个最好的方法是确保问题不会扩散 Dispose不应抛出任何异常。如果它写的不好,那么
try { some.Dispose(); } catch {}
应该足够了
因此,如果您的程序由于Dispose()中未处理的异常而崩溃-在我的例子中,这是因为关闭表单时线程访问UI元素。我通过中止窗体关闭上的线程来解决它。(“FormClosing”事件)
@如果您隐藏/处理从dispose抛出的任意异常,那么最终可能会泄漏内存、句柄或操作系统锁。所有这些都会对你的过程造成严重破坏。非常正确。我假设他想不惜一切代价让这一过程继续下去,但我可能应该说出来。他最好的办法肯定是扔掉控制权或让第三方修复它。您关于“难以调试”的评论是一个很好的观点。确实,在可能的情况下,应该设计Dispose的语义,以便它不会失败,但现实世界并不总是那么美好。例如,处理一个文件应该确保所有挂起的数据都被写入。如果数据没有被写入,那就是一个问题,需要以某种方式进行沟通。我建议除非某些东西真的错了,否则不应该抛出异常,但如果某些东西真的错了,就不应该忽略它。我明白你的意思。然而,Dispose不应该抛出任何异常。可以强制将挂起的数据写入