Is there a situation in which Dispose won't be

2019-01-22 05:07发布

This was a telephone interview question I had: Is there a time when Dispose will not be called on an object who's scope is declared by a using block?

My answer was no - even if an exception happens during the using block, Dispose will still be called.

The interviewer disagreed and said if using is wrapped in a try-catch block then Dispose will not be called by the time you enter the catch block.

This goes contrary to my understanding of the construct, and I haven't been able to find anything that backs up the interviewers point of view. Is he correct or might I have misunderstood the question?

标签: c# dispose using
8条回答
老娘就宠你
2楼-- · 2019-01-22 05:34

Four things that will cause Dispose to not be called in a using block:

  1. A power failure on your machine when inside the using block.
  2. Your machine getting melted by an atomic bomb while in the inside of the using block.
  3. Uncatchable exceptions like StackOverflowException, AccessViolationException and possibly others.
  4. Environment.FailFast
查看更多
ら.Afraid
3楼-- · 2019-01-22 05:36
using (var d = new SomeDisposable()) {
    Environment.FailFast("no dispose");
}
查看更多
劳资没心,怎么记你
4楼-- · 2019-01-22 05:39
void Main()
{
    try
    {
        using(var d = new MyDisposable())
        {
            throw new Exception("Hello");
        }
    }
    catch
    {
        "Exception caught.".Dump();
    }

}

class MyDisposable : IDisposable
{
    public void Dispose()
    {
        "Disposed".Dump();
    }
}

This produced :

Disposed
Exception caught

So I agree with you and not with the smarty interviewer...

查看更多
劳资没心,怎么记你
5楼-- · 2019-01-22 05:41

Bizarrely I read about a circumstance where Dispose won't get called in a using block just this morning. Checkout this blog on MSDN. It's around using Dispose with IEnumerable and the yield keyword, when you don't iterate the entire collection.

Unfortunately this doesn't deal with the exception case, honestly I'm not sure about that one. I would have expected it to be done but maybe it's worth checking with a quick bit of code?

查看更多
可以哭但决不认输i
6楼-- · 2019-01-22 05:46

Yes there is a case when dispose won't be called... you are over thinking it. The case is when the variable in the using block is null

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

using (var disp = foo.factory())
{
    //do some stuff
}

will not throw an exception but would if dispose was called in every case. The specific case that your interviewer mentioned is wrong though.

查看更多
贪生不怕死
7楼-- · 2019-01-22 05:49

The using block gets turned by the compiler into a try/finally block of its own, within the existing try block.

For example:

try 
{
    using (MemoryStream ms = new MemoryStream())
        throw new Exception();
}
catch (Exception)
{
    throw;
}

becomes

.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

The compiler won't rearrange things. So it happens like this:

  1. Exception is thrown in, or propagates to, the using block's try part
  2. Control leaves the using block's try part, and enters its finally part
  3. Object is disposed by the code in the finally block
  4. Control leaves the finally block, and the exception propagates out to the outer try
  5. Control leaves the outer try and goes into the exception handler

Point being, the inner finally block always runs before the outer catch, because the exception doesn't propagate til the finally block finishes.

The only normal case where this won't happen, is in a generator (excuse me, "iterator"). An iterator gets turned into a semi-complicated state machine, and finally blocks are not guaranteed to run if it becomes unreachable after a yield return (but before it has been disposed).

查看更多
登录 后发表回答