Brain Teaser #3


This brain teaser also comes courtesy of Simeon Cran.

What will the following code output? When you think you know, copy the code into Foo.cs and run “csc Foo.cs” and then run “Foo.exe”. Did it output what you expected? The brain teaser is to come up with the correct explanation for why the program outputs what it does.

using System;

struct Foo : IDisposable
{
    bool disposed;
    
    public void Dispose()
    {
        disposed = true;
        Console.WriteLine("Disposed");
    }

    static void Main(string[] args)
    {
        Foo foo;
        using (foo = new Foo())
        {
        }
        Console.WriteLine(foo.disposed);
        using (foo = new Foo())
        {
            foo.Dispose();
        }
        Console.WriteLine(foo.disposed);
    }
}
Comments (10)

  1. Anonymous says:

    Make your struct to a class and it should work as expected. Structs are copy by value and not reference.

  2. Ben Martens says:

    Thanks for the reply. You are correct that a class does not exhibit this behavior, but the brain teaser here is WHY. You mention a copy, but where/why does that copy happen?

    Answer will be posted on Tuesday.

  3. Ryan Heaney says:

    The using statements seem to do everything on local copies of the structs, so it would probably make their copies right at the start of the using block (but after the new). The copies are disposed and not the outside declared foo.

    In the second example, it actually seems as if calling the dispose explicitly is calling it on the original instance made by the second new, but then the using’s call to dispose calls it on its local copy of foo.

    Looking at the IL code for this, it does look like stack space for three instances of Foo are allocated and foo2 and foo3 are using within the first and second using’s respectively.

    Seems like structs and usings are quite unpredictable when it comes to situations like this.

  4. Anonymous says:

    You’ve been kicked (a good thing) – Trackback from DotNetKicks.com

  5. In the adcquisition phase of the using de disposable element is a new Structure Foo, the call to Dispose Method is not in foo.

    In the second using, you use de foo in using try phase and set dispose to TRUE..

    This code is similar to

    Foo foo;

    Foo newfoo = new Foo();

    using(foo = newfoo)

    {}

    //Call to newfoo.Dispose()

    using(foo = new Foo())

    {

      foo.Dispose();//set disposed = true

    }

    Unai

  6. Anonymous says:

    To call IDisposable, foo is boxed resulting in the heap instance changing its "disposed" value. The value on the stack is not affected.

    Similar to this:

    int i = 5;

    object o = i;

    o = ((int)o) + 1;

    Console.WriteLine(o.ToString());

    Console.WriteLine(i.ToString());

    Raj

  7. Anonymous says:

    Raj, your explanation is great, but it does not explain why the last time the value of foo.disposed is displayed it is ‘true’. If the boxed instances are the only instances affected, then why does the non-boxed foo change?

  8. Ben Martens says:

    Boxing is not the answer. If it was boxing, this code would print “Hits: 2” but it doesn’t:

    using System;

    struct Foo : IDisposable

    {

       int hitCounter;

       public void HitMe()

       {

           hitCounter++;

       }

       public void Dispose()

       {

           Console.WriteLine("Hits: " + hitCounter);

       }

       static void Main(string[] args)

       {

           Foo foo;

           using (foo = new Foo())

           {

               foo.HitMe();

               foo.HitMe();

           }

       }

    }

  9. Anonymous says:

    Chris,

    My intial thinking was that since the using statement

    Foo foo;

    using (foo = new Foo())

    {

    }

    translates to the following:

    Foo foo = new Foo();

    try

    {

    foo.Dispose();
    

    }

    finally {

    ((IDisposable)foo).Dispose();
    

    }

    I thought that foo would get boxed. But that is apparently not the case as Ben posted above. The complier will call Dispose directly without boxing it.

    My thinking now, after reading the C# specs, is that, because of a resource aqcuisition, the foo in

    using (foo = new Foo()) is readonly and is not accessible to the embedded statement. Therefore the compiler makes an extra copy of foo on the stack before the embeded statement is first hit.

  10. Anonymous says:

    Ultram. Can you snort ultram. Ultram side effects.

Skip to main content