The Using Statement And Disposable Value Types

comments edit

A while ago Ian Griffiths wrote about an improvement to his TimedLock class in which he changed it from a class to a struct. This change resulted in a value type that implements IDisposable. I had a nagging question in the back of my mind at the time that I quickly forgot about. The question is wouldn’t instances of that type get boxed when calling Dispose?

So why would I wonder that? Well let’s take a look at some code and go spelunking in IL. The following humble struct is the star of this investigation.

struct MyStruct : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing");
    }
}

Let’s write an application that will instantiate this struct and call its Dispose method via the interface.

public class App
{
    public void DemoDisposable()
    {
        IDisposable disposable = new MyStruct();
        DisoseIt(disposable);
    }

    public void DisoseIt(IDisposable disposable)
    {
        disposable.Dispose();
    }
}

Finally we will take our trusty Reflector out and examine the IL (I will leave out the method header).

.maxstack 2
.locals init (
    [0] [mscorlib]System.IDisposable disposable1,
    [1] NeverLockThis.MyStruct struct1)
L_0000: ldloca.s struct1
L_0002: initobj NeverLockThis.MyStruct
L_0008: ldloc.1 
L_0009: box NeverLockThis.MyStruct
L_000e: stloc.0 
L_000f: ldarg.0 
L_0010: ldloc.0 
L_0011: call instance void 
    NeverLockThis.App::DisoseIt([mscorlib]System.IDisposable)
L_0016: ret 

Notice the bolded line has a boxing instruction. As we can see, our struct gets boxed before the Dispose method is called.

The using statement requires that the object provided to it implements IDisposable. Here is a snippet from the MSDN2 docs on the subject.

The using statement allows the programmer to specify when objects that use resources should release them. The object provided to the using statement must implement the IDisposable interface. This interface provides the Dispose method, which should release the object's resources.

I wondered if the using statement enforced the IDisposable constraint in the same way a method would. Let’s find out. We will add the following new method to the App class.

public void UseMyStruct()
{
    MyStruct structure = new MyStruct();
    using (structure)
    {
        Console.WriteLine(structure.ToString());
    }
}

This code now implicitely calls the Dispose method via the using block. Cracking it open with Reflector reveals...

.maxstack 1
.locals init (
    [0] NeverLockThis.MyStruct struct1,
    [1] NeverLockThis.MyStruct struct2)
L_0000: ldloca.s struct1
L_0002: initobj NeverLockThis.MyStruct
L_0008: ldloc.0 
L_0009: stloc.1 
L_000a: ldloca.s struct1
L_000c: constrained NeverLockThis.MyStruct
L_0012: callvirt instance string object::ToString()
L_0017: call void [mscorlib]System.Console::WriteLine(string)
L_001c: leave.s L_002c
L_001e: ldloca.s struct2
L_0020: constrained NeverLockThis.MyStruct
L_0026: callvirt instance void 
    [mscorlib]System.IDisposable::Dispose()
L_002b: endfinally 
L_002c: ret 
.try L_000a to L_001e finally handler L_001e to L_002c

As you can see, there is no sign of a box statement anywhere to be seen. Forgive me for ever doubting you .NET team. As expected, it does the right thing. I just had to be sure. But do realize that if you pass in a value type that implements IDisposable to a method that takes in IDisposable, a box instruction will occur.

Comments