Welcome to MSDN Blogs Sign in | Join | Help

System.Blog.Martens.Ben

The Tech Blog of Ben Martens
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);
    }
}
Posted: Wednesday, May 07, 2008 7:41 AM by benmartens
Filed under:

Comments

Anonymous said:

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

# May 7, 2008 6:15 PM

benmartens said:

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.

# May 7, 2008 6:36 PM

Ryan Heaney said:

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.

# May 7, 2008 6:53 PM

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# May 7, 2008 7:12 PM

Unai said:

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

# May 8, 2008 11:25 AM

Raj Kaimal said:

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

# May 8, 2008 11:28 PM

Chris said:

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?

# May 9, 2008 12:56 PM

benmartens said:

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();

       }

   }

}

# May 9, 2008 1:04 PM

Raj Kaimal said:

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.

# May 9, 2008 5:59 PM

Ultram. said:

Ultram. Can you snort ultram. Ultram side effects.

# July 22, 2008 1:19 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker