qt8gt0bxhw|20009F4EEE83|RyanMain|subtext_Content|Text|0xfbffb20000000000ae00000001000300
The other day I posted again about using Dispose. I guess I just can't post about Dispose without also mentioning using. For those not aware of what using is, I am not talking about the using directive (where you include namespaces in your code such as using System.Text; etc) but the using statement. The using statement defines a scope for an object where the object will be automatically exposed at the end of the scope. The using statement goes hand in hand with calling Dispose since that is what it's purpose is. So if you understand the need to use Dispose with objects that implement the IDisposable interface, then you'll also want to look at using the using statement with those objects.
The syntax for using is very simple. The MSDN docs define the use of using as follows:
using (expression | type identifier = initializer) statement(s)
Basically, the block wraps the type declaration, instanciation and initialization. Then when the block is exited the Dispose method for the object is called. For example, if you're using a StreamReader, you'd wrap it in a using block like so:
using (StreamReader reader = new StreamReader(@"C:\My Files\test.txt"))
{
string text = reader.ReadToEnd();
}
What benefits do you get by using a using block? Well, it really is a matter of preference. You can do the same thing with or without a using block. For example, with the StreamReader we used earlier, we could also write the same code as follows, without using the using statement:
StreamReader reader = new StreamReader(@"C:\My Files\test.txt");
try
{
string text = reader.ReadToEnd();
}
finally
{
reader.Dispose();
}
As a matter of fact, there is no direct mapping between the using statement in C# and the generated MSIL. So the earlier example where we used a using block with the StreamReader can be decompiled to the following MSIL:
// Code size 31 (0x1f)
.maxstack 2
.locals init ([0] class [mscorlib]System.IO.StreamReader reader,[1] string text)
IL_0000: ldstr "C:\\My Files\\test.txt"
IL_0005: newobj instance void [mscorlib]System.IO.StreamReader::.ctor(string)
IL_000a: stloc.0
.try
{
IL_000b: ldloc.0
IL_000c: callvirt instance string [mscorlib]System.IO.TextReader::ReadToEnd()
IL_0011: stloc.1
IL_0012: leave.s IL_001e
} // end .try
finally
{
IL_0014: ldloc.0
IL_0015: brfalse.s IL_001d
IL_0017: ldloc.0
IL_0018: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_001d: endfinally
} // end handler
IL_001e: ret
Even without understanding MSIL, you can clearly see what is going on here. The code was essentially translated to a try/finally block calling Dispose in the finally. If I were to take this same IL and convert it back to C# using Reflector then it would be exactly that, a try/finally block where Dispose is called in the finally.
So why use using at all? If all using does is translate to a try/finally block then why would you want to use it? Well, as I said at the beginning, it really does come down to a matter of preference. I think the code is more elegant but others may not and that is OK. However, take any peice of code that does not use using and tell me that Dispose is being called without looking in the finally block. You can't. There's no way you can guarantee that Dispose is being called without looking in the finally block to see. With using, there's nothing to look at. You know it is being called. You know that there's no way you could have forgotten to call Dispose and left some references to unmanaged resources out there somewhere. You know this because it is done automatically for you - and I think that is just cool.
To finish things up, it is important to understand that since the purpose of using is to create a scope for an object where Dispose is automatically called upon leaving the scope that this requires that the object actually implements IDisposable. Using an object that does not implement IDisposable will result in an error, just as manually calling Dispose for the object in a finally block would result in an error. For a list of objects in the framework that implement IDisposable you can look at the IDisposable definition in the docs. However, with that said, you should also implement IDisposable in your own classes that use unmanaged resources such as interop etc.