Recipe 3.27. Assuring an Object's Disposal
problem
You require a way to always have the
Dispose
method of an object called when that object's work is done or it goes out of scope.
Solution
Use the
using
statement:
using System;
using System.IO;
// …
using(FileStream FS = new FileStream("Test.txt", FileMode.Create))
{
FS.WriteByte((byte)1);
FS.WriteByte((byte)2);
FS.WriteByte((byte)3);
using(StreamWriter SW = new StreamWriter(FS))
{
SW.WriteLine("some text.");
}
}
Discussion
The
using
statement is very easy to use and saves you the hassle of writing extra code. If the Solution had not used the
using
statement, it would look like this:
FileStream FS = new FileStream("Test.txt", FileMode.Create);
try
{
FS.WriteByte((byte)1);
FS.WriteByte((byte)2);
FS.WriteByte((byte)3);
StreamWriter SW = new StreamWriter(FS);
try
{
SW.WriteLine("some text.");
}
finally
{
if (SW != null)
{
((IDisposable)SW).Dispose( );
}
}
}
finally
{
if (FS != null)
{
((IDisposable)FS).Dispose( );
}
}
Several points to note about the
using
statement:
-
There is a
using
directive, such as
using System.IO;
which should be differentiated from the
using
statement. This is
potentially
confusing to developers first getting into this language.
-
The variable(s) defined in the
using
statement clause must all be of the same type, and they must have an initializer. However, you are allowed multiple
using
statements in front of a single code block, so this isn't a significant restriction.
-
Any
variables
defined in the
using
clause are
considered
read-only in the body of the
using
statement. This
prevents
a developer from inadvertently switching the variable to refer to a different object and
causing
problems when an attempt is made to dispose of the object that the variable initially referenced.
-
The variable should not be declared outside of the
using
block and then
initialized
inside of the
using
clause.
This last point is described by the following code:
FileStream FS;
using(FS = new FileStream("Test.txt", FileMode.Create))
{
FS.WriteByte((byte)1);
FS.WriteByte((byte)2);
FS.WriteByte((byte)3);
using(StreamWriter SW = new StreamWriter(FS))
{
SW.WriteLine("some text.");
}
}
For this example code, you will not have a problem. But consider that the variable
FS
is usable outside of the
using
block. Essentially, you could
revisit
this code and modify it as
follows
:
FileStream FS;
using(FS = new FileStream("Test.txt", FileMode.Create))
{
FS.WriteByte((byte)1);
FS.WriteByte((byte)2);
FS.WriteByte((byte)3);
using(StreamWriter SW = new StreamWriter(FS))
{
SW.WriteLine("some text.");
}
}
FS.WriteByte((byte)4);
This code compiles but throws an
ObjectDisposedException
on the last line of this code snippet because the
Dispose
method has already been called on the
FS
object. The object has not yet been collected at this point and still remains in memory in the disposed state.
See Also
See Recipes 3.29 and 3.31; see the "IDispose Interface," "Using foreach with Collections," and "Implementing Finalize and Dispose to Clean up Unmanaged Resources" topics in the MSDN documentation.
|