C#: IDisposable classes need 'using' clause?
If am using 开发者_开发技巧an IDisposable
class, should I always use a using
clause, for example:
using (MyClass myclass = new MyClass())
{
...
}
The using
statement ensures that Dispose
is called to free the resources even if an exception occurs or if the object goes out of scope.
This is simpler than using the following code block,
try
{
...
}
catch()
{
// Handle exception
}
finally
{
// Free resources by calling Dispose()
}
Note
The catch
block is not necessary if one does not want to handle the exception
. In that case, a try
...finally
block would suffice (as pointed in other answers).
Alternative way
You can create multiple, instance of the disposable object in the same using statement, for example
using(MyClass myclass1 = new MyClass(),
MyClass myclass2 = new MyClass())
{
...
}
Example
From Understanding the 'using' statement in C#
using (MyResource myRes = new MyResource())
{
myRes.DoSomething();
}
gets translated to (by the CLR)
MyResource myRes= new MyResource();
try
{
myRes.DoSomething();
}
finally
{
// Check for a null resource.
if (myRes!= null)
{
// Call the object's Dispose method.
((IDisposable)myRes).Dispose();
}
}
You can have a look at the generated MSIL in the link specified at the beginning of the example.
More information
- using Statement (C# Reference)
- Great Uses of Using Statement in C#
- Implementing a Dispose Method
- Implementing Finalize and Dispose to Clean Up Unmanaged Resources
- Related: using Directive (C# Reference)
It makes it much more readable code. So, as a rule you should declare and instantiate the object in a using block. It ensures that the Dispose method is called even if an exception happens. At compile time the same expression would look something like this:
{
MyClass myclass = new MyClass ();
try {
//Do something with myclass
}
finally {
if (myclass != null)
((IDisposable)myclass).Dispose();
}
}
If a class implements IDisposable
you should always call Dispose
when you are done with the object. C# provides the using statement as syntactic sugar to make this easier. So, it's not required to use using
to call Dispose
. That said, it is the de facto standard way to invoke Dispose
. i.e. if your code is not using using
and is instead invoking Dispose
in finally blocks it will look a bit odd to experienced coders.
The expansion of your using block (assuming MyClass is a reference type) is:
{
MyClass myclass = new MyClass();
try {
//...
}
finally {
if (myclass != null) ((IDisposable)myclass).Dispose();
}
}
The using
version is easier to code and easier to read.
Simply put, if it implements IDisposable, and you want to have that method called (also on exceptions in the finally block when you are doing. Use using.
The GC will NOT call Dispose() for you! Some classes implement a finalizer ~ClassName() but try to avoid this because it has many side effects and is not obvious.
You can read an using statement as:
TestClass test = new TestClass();
try
{
test.DoSomething();
}
finally
{
test.Dispose();
}
You should always do it so long as the lifetime of the object is short enough that this is possible. Sometimes you have to handle the cleanup separately because you have objects with a long lifetime that will persist long after the block of code is done.
There are three scenarios when an IDisposable object is created:
- The object will be needed for a little while, but won't be needed after the current block of code exits.
- The object will be given to the caller of the current routine, which will then be responsible for it.
- The class which is responsible for the IDisposable object stores it in a field so it will be available for use in future method/property calls.
In scenario #1, use a "using" block to create the object; its cleanup will be handled automatically.
In scenario #2, use a "try-finally" block with an "ok" variable that's set to "False" initially but is set to "True" at the end of the "try" or just before any return; in the finally, if "oK" is false, call Dispose to ensure the partially-constructed object gets cleaned up.
In scenario #3, store the object in a field as soon as it's created, and define an IDisposable.Dispose method which will copy the value in the field to a variable, null out the field, and if the variable was non-null, Dispose it (if there's any possibility of multiple threads calling Dispose simultaneously, use Interlocked.Exchange to latch and clear the field). Constructors should also be protected as would functions in scenario #2.
the using clause is syntax sugar on the following code block:
MyClass myclass = null;
try
{
myclass = new MyClass();
//do work
}
finally
{
if(myclass != null)
myclass.Dispose();
}
If a class implements IDisposable you should ensure that Dispose is called when you are done using it. The using clause is just an easy way to do it. So you don't have have to use "using" to do it, but you should ensure that it is called.
精彩评论