Thursday, 5 June 2008

Finally The Truth About "using" and Dispose()

The Problem

During the good chat with some fellow students last night, the top of whether or not "using" should be used over an explicit Dispose() method call when using objects like Graphics came up.

Now, this one has been bugging me for some time, and I have never actually sat down to investigate it, so I thought I should. I have been told on several occasions that I should use "using", but when questioning why, I get no real answer that makes any sense. Seems like a classic "every knows the best practice but doesn't understand the practice or why its best" scenario!

So, I made some very small code samples, one using "using" the other using Dispose(). Here are the samples FYI:

"using" Code

   1: private void Form1_Load(object sender, EventArgs e)
   2: {
   3:     using (Graphics g = Graphics.FromHwnd(this.Handle))
   4:     {
   5:         MessageBox.Show("this is between the graphics instantiation and disposal");
   6:     }
   7: }

Dispose() Code

   1: private void Form1_Load(object sender, EventArgs e)
   2: {
   3:     Graphics g = Graphics.FromHwnd(this.Handle);
   4:     MessageBox.Show("this is between the graphics instantiation and disposal");
   5:     g.Dispose();
   6: }

As you can see, nothing fancy! And here are the results (intermediate language - "IL") courtesy of ILDASM:

"using" IL

   1: .method private hidebysig instance void  Form1_Load(object sender,
   2:                                                     class [mscorlib]System.EventArgs e) cil managed
   3: {
   4:   // Code size       36 (0x24)
   5:   .maxstack  1
   6:   .locals init ([0] class [System.Drawing]System.Drawing.Graphics g)
   7:   IL_0000:  ldarg.0
   8:   IL_0001:  call       instance native int [System.Windows.Forms]System.Windows.Forms.Control::get_Handle()
   9:   IL_0006:  call       class [System.Drawing]System.Drawing.Graphics [System.Drawing]System.Drawing.Graphics::FromHwnd(native int)
  10:   IL_000b:  stloc.0
  11:   .try
  12:   {
  13:     IL_000c:  ldstr      "this is between the graphics instantiation and dis"
  14:     + "posal"
  15:     IL_0011:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)
  16:     IL_0016:  pop
  17:     IL_0017:  leave.s    IL_0023
  18:   }  // end .try
  19:   finally
  20:   {
  21:     IL_0019:  ldloc.0
  22:     IL_001a:  brfalse.s  IL_0022
  23:     IL_001c:  ldloc.0
  24:     IL_001d:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
  25:     IL_0022:  endfinally
  26:   }  // end handler
  27:   IL_0023:  ret
  28: } // end of method Form1::Form1_Load

Dispose() IL

   1: .method private hidebysig instance void  Form1_Load(object sender,
   2:                                                     class [mscorlib]System.EventArgs e) cil managed
   3: {
   4:   // Code size       30 (0x1e)
   5:   .maxstack  1
   6:   .locals init ([0] class [System.Drawing]System.Drawing.Graphics g)
   7:   IL_0000:  ldarg.0
   8:   IL_0001:  call       instance native int [System.Windows.Forms]System.Windows.Forms.Control::get_Handle()
   9:   IL_0006:  call       class [System.Drawing]System.Drawing.Graphics [System.Drawing]System.Drawing.Graphics::FromHwnd(native int)
  10:   IL_000b:  stloc.0
  11:   IL_000c:  ldstr      "this is between the graphics instantiation and dis"
  12:   + "posal"
  13:   IL_0011:  call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)
  14:   IL_0016:  pop
  15:   IL_0017:  ldloc.0
  16:   IL_0018:  callvirt   instance void [System.Drawing]System.Drawing.Graphics::Dispose()
  17:   IL_001d:  ret
  18: } // end of method Form1::Form1_Load

Conclusion

So, as you can see, the "using" method wraps the code inside the scope of the statement within a "try" block of a "try/finally", placing the Dispose() call in the finally, meaning it will always get executed even if an Exception is thrown. However, using the explicit Dispose() method, if an Exception is thrown, then it will not get disposed of, meaning it could be sat on the heap for a few milliseconds or even a few minutes, not good!

I guess this is why MSDN refers to the "using" statement as the "convenient syntax for correct use of IDisposable objects".

I believe the VB developers out there do not have an equivalent available, so make sure you are wrapping all of your disposable objects calls in try/finally blocks!

Update: I have been told that VB.NET 2.0 does actually have a "using" statement as well, which looks surprisingly like the C# syntax! http://msdn.microsoft.com/en-us/library/htd05whh.aspx

Share:  digg it! del.icio.us furl Live Technorati Facebook

No comments:

Post a Comment