是否存在不会为"使用"块调用Dispose的情况?

Ada*_*ord 66 c# dispose using

这是我的电话采访问题:是否有时间不会在使用块声明范围的对象上调用Dispose?

我的回答是否定的 - 即使在使用块期间发生异常,仍会调用Dispose.

面试官不同意并说如果using包裹在try- catch块中,那么当你进入catch块时就不会调用Dispose.

这与我对构造的理解相反,我无法找到任何支持采访者观点的东西.他是正确的还是我误解了这个问题?

Øyv*_*hen 58

将导致Dispose无法在using块中调用的四件事:

  1. 使用块内部时机器出现电源故障.
  2. 你的机器在使用区域内部被原子弹熔化.
  3. 栋笃神探异常喜欢StackOverflowException,AccessViolationException其它可能的.
  4. Environment.FailFast

  • 按概率顺序列出;) (18认同)
  • 4.环境.下面列出的快餐.http://msdn.microsoft.com/en-us/library/ms131100.aspx (6认同)

Vde*_*edT 53

void Main()
{
    try
    {
        using(var d = new MyDisposable())
        {
            throw new Exception("Hello");
        }
    }
    catch
    {
        "Exception caught.".Dump();
    }

}

class MyDisposable : IDisposable
{
    public void Dispose()
    {
        "Disposed".Dump();
    }
}
Run Code Online (Sandbox Code Playgroud)

这产生了:

Disposed
Exception caught
Run Code Online (Sandbox Code Playgroud)

所以我同意你的意见,而不是聪明的面试官......

  • @Blindy"面试官不同意并说*如果使用包裹在try catch块中,那么当你进入catch块时,Dispose将不会被调用.*"我认为答案是一个非常好的反例,对于面试官的"理论" . (29认同)
  • 我相信正确的语法将是"异常被抓住" (8认同)
  • +1用于双重检查和使用LINQPad;) (7认同)
  • @blindy:Wouaw,真的很生气,抱歉.你们招人吗 ;-) (7认同)
  • 为什么这是公认的答案?这不是科学如何运作,你不只是选择一个特定的,量身定制的例子,并宣称你的理论是完美的...如果你想知道,你实际上完全错了,它可能是'Dispose`不要叫. (5认同)
  • @Will,Øyvind为我做了那个,为什么要重新输入?而且我从来没有尝试*证明*Dispose`不可能被调用,VdesmedT可以,并且他通过选择*working*示例来尝试它.我相信,我的观点是. (3认同)
  • 请原谅我,但字符串上的.Dump()是什么? (2认同)
  • @Blindy:Pfft.请将您与维基百科对科学方法定义的傲慢联系.说你可以反驳一个事实,因为在证据的中途,流星可能会破坏天花板并扰乱实验,就像你能得到的一样荒谬.我无法看到*VdesmedT做过的任何地方,他通过选择一个工作示例来尝试它.*再次,我问你是否可以请给我一个例子说明这可能发生的地方(不包括任何涉及哥斯拉的地方)? (2认同)

Ian*_*Ian 25

奇怪的是,我读到了今天早上Dispose不会在使用区块中被调用的情况.在MSDN上查看此博客.当你不迭代整个集合时,它使用Dispose with IEnumerable和yield关键字.

不幸的是,这并不涉及异常情况,说实话,我不确定那个.我原本预计它会完成,但也许值得用快速的代码检查一下?

  • 帖子中描述的行为非常符合逻辑.如果返回的迭代器的使用者不在`IEnumerator`上调用`Dispose`,则不会调用迭代器(yield)方法中的处理.换句话说:"在不调用Dispose的情况下,不会调用Dispose",这当然是微不足道的.解决方案:在使用块中包含`IEnumerator`的用法,或者使用C#`foreach`语句迭代它. (6认同)

svi*_*ick 21

有关电源故障,其他的答案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
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,它看起来像正确Foo发布m_resource,我们也using正确使用.但由于例外,从未调用Dispose()on Foo.在这种情况下的修复是使用终结器并在那里释放资源.

  • +1非常好的捕捉.实际上,我会说"修复"是根本不允许ctor中的非托管资源.应该使用延迟加载来完成对这些资源的所有访问. (4认同)

cHa*_*Hao 19

using块被编译器变成了try/ finally它自己的块,现有的try块.

例如:

try 
{
    using (MemoryStream ms = new MemoryStream())
        throw new Exception();
}
catch (Exception)
{
    throw;
}
Run Code Online (Sandbox Code Playgroud)

.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
Run Code Online (Sandbox Code Playgroud)

编译器不会重新排列内容.所以它发生如下:

  1. 异常被抛入或传播到using块的try部分
  2. 控制离开using块的try一部分,并进入其finally部分
  3. 对象finally块中的代码处理
  4. Control离开finally块,异常传播到外部 try
  5. Control离开外部try并进入异常处理程序

指出,内部finally块总是在外部之前运行catch,因为异常不会传播直到finally块完成.

这种情况不会发生的唯一正常情况是发生器(对不起,"迭代器").迭代器变成一个半复杂的状态机,finally如果在一个yield return(但在它被处理之前)它变得无法访问,则不能保证块运行.


adr*_*anm 13

using (var d = new SomeDisposable()) {
    Environment.FailFast("no dispose");
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您有更多信息,那就太好了:http://msdn.microsoft.com/en-us/library/ms131100.aspx (3认同)

Yau*_*aur 5

是的,有一种情况下不会调用处理......你在想它.情况是使用块中的变量null

class foo
{
    public static IDisposable factory()
    {
        return null;
    }
}

using (var disp = foo.factory())
{
    //do some stuff
}
Run Code Online (Sandbox Code Playgroud)

不会抛出异常,但会在每种情况下调用dispose.虽然你的面试官提到的具体案例是错误的.