[英]Is there a situation in which Dispose won't be called for a 'using' block?
这是我的电话面试问题:是否有一段时间不会对范围由 using 块声明的对象调用 Dispose?
我的回答是否定的 - 即使在 using 块期间发生异常,仍然会调用 Dispose。
面试官不同意并说如果using
包含在try
- catch
块中,那么在您进入 catch 块时不会调用 Dispose。
这与我对结构的理解背道而驰,我无法找到任何支持面试官观点的内容。 他是正确的还是我误解了这个问题?
导致 Dispose 在 using 块中不被调用的四件事:
StackOverflowException
、 AccessViolationException
和其他可能的异常。void Main()
{
try
{
using(var d = new MyDisposable())
{
throw new Exception("Hello");
}
}
catch
{
"Exception caught.".Dump();
}
}
class MyDisposable : IDisposable
{
public void Dispose()
{
"Disposed".Dump();
}
}
这产生了:
Disposed
Exception caught
所以我同意你而不是聪明的面试官......
奇怪的是,我今天早上读到了一种情况,即 Dispose 不会在 using 块中被调用。 在 MSDN 上查看此博客。 当您不迭代整个集合时,将使用 Dispose 与 IEnumerable 和 yield 关键字。
不幸的是,这并没有处理例外情况,老实说我不确定那个。 我原以为它会完成,但也许值得用一些代码快速检查一下?
关于电源故障、 Environment.FailFast()
、迭代器或using
null
作弊的其他答案都很有趣。 但我觉得奇怪的是,没有人提到我的想法是,当最常见的情况Dispose()
甚至不会在存在被称为using
:当内部的表达using
抛出异常。
当然,这是合乎逻辑的: using
的表达式抛出了一个异常,所以赋值没有发生,我们无法调用Dispose()
。 但是一次性对象可以已经存在,尽管它可以处于半初始化状态。 即使在这种状态下,它也已经可以容纳一些非托管资源。 这是正确实现一次性模式很重要的另一个原因。
有问题的代码示例:
using (var f = new Foo())
{
// something
}
…
class Foo : IDisposable
{
UnmanagedResource m_resource;
public Foo()
{
// obtain m_resource
throw new Exception();
}
public void Dispose()
{
// release m_resource
}
}
在这里,看起来Foo
正确释放了m_resource
并且我们也正确using
了。 但是由于异常,永远不会调用Foo
上的Dispose()
。 在这种情况下的解决方法是使用终结器并在那里释放资源。
using
块被编译器转换为它自己的try
/ finally
块,位于现有的try
块中。
例如:
try
{
using (MemoryStream ms = new MemoryStream())
throw new Exception();
}
catch (Exception)
{
throw;
}
变成
.try
{
IL_0000: newobj instance void [mscorlib]System.IO.MemoryStream::.ctor()
IL_0005: stloc.0
.try
{
IL_0006: newobj instance void [mscorlib]System.Exception::.ctor()
IL_000b: throw
} // end .try
finally
{
IL_000c: ldloc.0
IL_000d: brfalse.s IL_0015
IL_000f: ldloc.0
IL_0010: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0015: endfinally
} // end handler
} // end .try
catch [mscorlib]System.Exception
{
IL_0016: pop
IL_0017: rethrow
} // end handler
编译器不会重新排列东西。 所以它是这样发生的:
using
块的try
部分using
块的try
部分,进入它的finally
部分finally
块中的代码处理try
try
并进入异常处理程序重点是,内部finally
块总是在外部catch
之前运行,因为异常不会传播到finally
块完成。
唯一不会发生这种情况的正常情况是在生成器中(对不起,“迭代器”)。 迭代器变成了一个半复杂的状态机,如果在yield return
(但在它被处理之前)变得无法访问,则不能保证finally
块运行。
using (var d = new SomeDisposable()) {
Environment.FailFast("no dispose");
}
是的,在某些情况下不会调用 dispose ......你想多了。 情况是 using 块中的变量为null
class foo
{
public static IDisposable factory()
{
return null;
}
}
using (var disp = foo.factory())
{
//do some stuff
}
不会抛出异常,但如果在每种情况下都调用了 dispose 就会抛出异常。 不过,你的面试官提到的具体案例是错误的。
面试官说对了一部分。 Dispose
可能无法根据具体情况正确清理底层对象。
例如,如果在 using 块中引发异常,WCF 有一些已知问题。 你的面试官可能正在考虑这个。
这是 MSDN 上的一篇关于如何避免WCF使用块出现问题的文章。 这是Microsoft 的官方解决方法,尽管我现在认为该答案和这个答案的组合是最优雅的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.