7

As describe this article, about usage of using on IDisposable objects, it says one interesting words:

...using block, the Dispose method is automatically called sometime after the block ends. (It may not be immediate; it depends on the CLR.)

Interesting here is "It may not be immediate; it depends on the CLR". Can anyone provide more details on this? Because we have some strange situations where it seems that on code using(new MyDisposable()) {...}, after end of block } it does NOT immediately calls Dispose method on MyDisposable instance, but some time later.

UPDATE: Conclusion for me, it seems to me that i have problem elsewhere. I think that Dispose method can be called some time later after using block ends. But when it is not like that, than i must find problem somewhere else in my code. Thanks for responses!

psulek
  • 4,308
  • 3
  • 29
  • 37
  • 1
    I don't know, but that article is suspect considering it is wrong about what a using block 'exactly' compiles into. They don't even include the try/finally block. I stopped reading at that point. – Ed S. Oct 30 '10 at 21:08
  • 4
    Dispose IS called immediately when exiting the scope of a Using block, it's finalisers that are called much later, if at all. The article is simply wrong on this point. Ref: http://msdn.microsoft.com/en-us/library/yh598w02.aspx – Will Oct 30 '10 at 21:28
  • The author of that web page is *quite* clueless about what's really going on. Quote: "Dispose, Finalize, and destructors are all related, but that’s beyond our scope of discussion here". Yes, that's the part he didn't get. – Hans Passant Oct 30 '10 at 21:29
  • Unfortunately anyone can post incorrect dribble on these interwebs of ours. You have to be a little more critical of any single source of information. – Ed S. Oct 30 '10 at 21:44
  • I am critical about this article, thats why i wrote question here. My problem is that i think that such strange delayed execution of Dispose was in my case. But with these answers from you it seems that i have problem in somewhere else. – psulek Oct 31 '10 at 07:54
  • The point of the article was not so much the "when" of Dispose() being called, but more on the relationship between IDisposable vs. using(...). I've updated it to reference the MSDN documentation. The point of blogging is to learn as much as it is to teach :) – ashes999 Oct 31 '10 at 16:58
  • @ashes999, its ok, i just want to know where is the truth. Thanks to all for explanation. – psulek Oct 31 '10 at 19:59

5 Answers5

9
using (SomeDisposableResource resource = new SomeDisposableResource())
{
    // TODO: use the resource
}

is equivalent to:

SomeDisposableResource resource = new SomeDisposableResource();
try
{
    // TODO: use the resource
}
finally
{
    if (resource != null)
    {
        ((IDisposable)resource).Dispose();
    }
}

so, up to you to draw conclusions. Everything depends on how you define immediate. In a multithreaded environment other actions could be performed between the try block and the disposal of the resource but as it is wrapped in a finally block it is guaranteed that the Dispose method will be called.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • cool, I didn't know that - given that the poster could set a breakpoint in his dispose method and another on the line after the end of the using block, and should not have any doubts whatsover, is that what you are implying? – Aaron Anodide Oct 30 '10 at 21:14
  • Note that `resource` here is considered read-only inside the using construct. The null-test inside the `finally` block kicks in if the expression that produces the object, the `= new SomeDisposableResource()` in this example, calls a method that can return null. There's also some details regarding structs that aren't important for this case. In short, the call to `Dispose` happens as part of the `}` of the using construct. – Lasse V. Karlsen Oct 30 '10 at 21:18
  • Yes, what I meant was... If you take out the original `}` and substitute it with the finally-part of your answer, you can think of it as "being part of the `}`". It's a mental model of how the C# code executes, but yes, I know, there's no braces in IL :) – Lasse V. Karlsen Oct 30 '10 at 21:22
6

I'm a little skeptical of that statement, and think they meant something else (perhaps garbage collection). A using statement is just syntactic sugar for a try/finally block where the finally block calls dispose. Given this C#:

using (var fs = new FileStream("C:\\blah.txt", FileMode.CreateNew))
{
    fs.WriteByte(7);
}

The IL looks like this:

//snipped
L_000e: nop 
L_000f: ldstr "C:\\blah.txt"
L_0014: ldc.i4.1 
L_0015: newobj instance void [mscorlib]System.IO.FileStream::.ctor(string, valuetype [mscorlib]System.IO.FileMode)
L_001a: stloc.0 
L_001b: nop 
L_001c: ldloc.0 
L_001d: ldc.i4.7 
L_001e: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8)
L_0023: nop 
L_0024: nop 
L_0025: leave.s L_0037
L_0027: ldloc.0 
L_0028: ldnull 
L_0029: ceq 
L_002b: stloc.1 
L_002c: ldloc.1 
L_002d: brtrue.s L_0036
L_002f: ldloc.0 
L_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0035: nop 
L_0036: endfinally 
L_0037: nop 
L_0038: nop 
L_0039: ret 
.try L_001b to L_0027 finally handler L_0027 to L_0037

Notice on the last line it's just a .try and .finally. This is also indicated in The using statement from the C# spec.

0xced
  • 25,219
  • 10
  • 103
  • 255
vcsjones
  • 138,677
  • 31
  • 291
  • 286
1

Strange... very strange...

The article is probably wrong. The using statement is compiled exactly as

MyDisposableObject obj = new MyDisposableObject()
try
{
    obj.Use();
}
finally
{
    if (obj!=null) obj.Dispose();
}

The Dispose method is explicitly called in the finally block, unlike the destructor/Finalize method which is called prior to collection but at GC's discretion.

I think it's an error in the article. At most... that sometime may refer to thread scheduling. If CLR decides to schedule other threads once hit the finally, then you might wait up some very little time on 100% loaded CPU and higher-priority tasks running to run Dispose.

It's important for Dispose to be synchronous!!! Think about this example about managed resources

public void Log(string message)
{
    using(StreamWriter sw = new StreamWriter(File.Append(path)))
    {
        sw.WriteLine(message);
    }
}
public static void Main()
{
    Log("Hello");
    Log("World");
}

The Dispose call, on streams and files, actually closes them. If what's written in the article was ever true, you would be calling the second Log with an open file, thus causing IOException immediately!

usr-local-ΕΨΗΕΛΩΝ
  • 26,101
  • 30
  • 154
  • 305
  • 1
    Using's do a null check before calling dispose, which is why this construct is also useful for disposing previously created objects, if not null: `using(x) {}` - beats having to write `if (x != null) x.Dispose();` everywhere, though not quite as clear. – Will Oct 30 '10 at 21:17
  • Yea I forgot. Actually it should even cast to IDisposable, otherwise the code won't work with classes that explicitly implement IDisposable like Stream, but that's still the sense ;) – usr-local-ΕΨΗΕΛΩΝ Oct 30 '10 at 21:22
  • It won't always cast to IDisposable though, look at the IL for `using` on a struct implementing IDisposable with Dispose being a public method of the struct as well. – Lasse V. Karlsen Oct 30 '10 at 21:24
0

I don't know where that comes from, and that contradicts everything else I have seen about the using statement, for example this article that says that a using block like this:

using (Font font1 = new Font("Arial", 10.0f)) {
  byte charset = font1.GdiCharSet;
}

is implemented like this:

Font font1 = new Font("Arial", 10.0f);
try {
    byte charset = font1.GdiCharSet;
} finally {
  if (font1 != null) {
    ((IDisposable)font1).Dispose();
  }
}

So, the Dispose method will be called before the next statement following the using block.

The person writing the article might have it confused with how an object will be garbage collected some time after it's not used any more, or how disposable objects that are not disposed have their finalizer called by a background thread.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
0

Theory meets facts using (no pan intended) .NET 5 and xunit.

While the Try..Catch..Finally construct disposes the object before the final assertion, the Using block does not.

public class DisposableUnitTests
{

    private class DisposableObject : IDisposable
    {

        public bool Disposed { get; set; }

        protected virtual void Dispose( bool disposing )
        {
            if ( disposing )
                this.Disposed = true;
        }

        public void Dispose()
        {
            this.Dispose( true );
        }
    }

    [Fact]
    public void CanTryCatchFinallyDispose()
    {
        DisposableObject Target = new DisposableObject();
        try
        {
            Assert.False( Target.Disposed, $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
        }
        catch ( Exception )
        {

            throw;
        }
        finally
        {
            ((IDisposable)Target)?.Dispose();
        }
        Assert.True( Target.Disposed , $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
    }

    [Fact]
    public void CanUsingBlockDispose()
    {
        using DisposableObject Target = new DisposableObject();
        {
            Assert.False( Target.Disposed, $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
        }
        Assert.False( Target.Disposed, $"Using block not disposing {nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
    }

}
David
  • 35
  • 3