Resource Management In C#

Sometimes it’s inadvisable to release resource in a destructor; some resources are just too valuable and too scarce to lie around unreleased for arbitrary lengths of time. In the situations, your only option is to release the resource yourself with disposal method that disposes resources. If a class has a disposal method, you can call it explicitly and control when the resource is released.
The disposal method pattern
TextReader class from the System.IO namespace contains a virtual method called Close(). The StreamReader and String Reader classes both drive from TextReader and both override the Close method:
class ResourceMgmt
{
public void DoSomething()
{
TextReader reader = new StreamReader(filename);
string line;
while ((line = reader.ReadLine()) != null)
{
Consle.WriteLine(line);
}
reader.Close();
}
}
It’s important to call Close when you have finished with reader to release the file handle(and encoding) resources. However, it isn’t exception-safe. If the call to ReadLine or WriteLine throws exception, the call to Close will not happen!
Exception-safe disposal
One way to ensure that disposal method always call, regardless of whether there is an exception is to call it inside a try…finally block:
class Resource
{
public void DoSomething()
{
TextReader reader = new StreamReader(filename);
try
{
string line;
while ((line = reader.ReadLine()) != null)
{
Consle.WriteLine(line);
}
}
finally
{
reader.Close();
}
}
}
However, it has several drawbacks that make it a less than ideal solution:
• It quickly get unwieldy if you have to dispose of more than one resource(nested try and finally blocks)
• In some cases you might have to modify the code(reorder the declaration of resource reference, remember to initialize the reference to null and remember to check the reference isn’t null in the finally block).
• The solution is hard to understand and must be repeated everytime.
• The reference to the resource remains in scope after the finally block which means you can accidentally use the resource after it has been release.
The using statement is designed to solve all these problems!
The using statement
using (type variable = initialization) embeddedStatement
such using statement is precisely equivalent to:
public void ReturnResource()
{
//type variable = initialization;
try
{
//embeddedStatement
}
finally
{
if (varaible != null)
{
 			((IDisposable)variable).Dispose();
}
}
}
This equivalence means that the variable you declare in a using statement must be of a type that implements IDisposable interface which live in System namespace and contains just one method called Dispose:
namespace System
{
interface IDisposable
{
void Dispose();
}
}
You can use a using statement as a clean, exception-safe, robust way to ensure that a resource is always automatically released, you just need to make sure that:
• The class containing the diposal method(Close() for TextReader) implements the IDisposable interfaces(as TextReader does).
• The class implements Dispose to call the disposal method(TextReader.Dispose() calls TextReader.Close()):
abstract class TextReader : IDispoable
{
public virtual void Dispose()
{
//call Close()
}
public virtual void Close()
{
}
}
Here is the best way to make sure that you r code always calls Close ona TextReader:
using (TextReader reader = new StreamReader(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
This approach sloves all of the problems that existed in the manual try…finally solutions:
• Scales well if you need to dispose of multiple resources.

• Doesn’t distort the logic of the program code.

• Nicely abstracts away the problem and avoid repetition.

• Is robust.

Adapting to IDisposable
It’s instructive to consider how you could have used a using statement to ensure that it always call TextReader.Close() if the TextReader class didn’t implement the IDisposable interface:
struct AutoClose : IDisposable
{
private readonly TextReader target;
public void AutoClosing(TextReader target)
{
this.target = target;
}
public TextReader GetTextReader()
{
return target;
}
public void Dispose()
{
target.Close();
}
}
• AutoClosing doesn’t extend the TextReader class. This avoids the need to replicate all the TextReader constructors inside AutoClosing struct.
• AutoClosing isn’t designed to be a base class and doesn’t extend a class. You want the memory deallocated as soon as it goes out of scope. It is best implemented as a struct rather than a class.
You can rewirte the GetTextReader accessor method as a read-only property:
using(AutoClosing safe = new AutoClosing(new StreamReader(filename)))
{
TextReader reader = safe.GetTextReader();
string line;
while((line = reader.ReadLine() != null)
{
Console.WriteLine(line);
}
}
Notice how the reader variable is still in the scope of using statement!
Calling a disposal method from a destructor
The trade off in deciding whether to use destructors or disposal methods is this: A call to a destructor will happen but you just don’t know when, while you know exactly when a call to a disposal method happens but yo ujust can’t be sure that it will happen because you might forget to call the method! So it is possible to ensure that a disposal method is always called by having “backup”:
class Example : IDisposable
{
private Resource scarce;
private bool disposed = false;
~Example()
{
Dispose();
}
public virtual void Dispose()
{
if (!disposed)
{
try
{
//release scarce resource here
}
finally
{
disposed = true;
GC.SuppressFinalize(this);
}
}
}
public void SomeBehavior()
{
CheckIfDisposed();
}
private void CheckIfDisposed()
{
if (disposed)
{
throw new ObjectDisposedException("Example");
}
}
}
}
• The class implements IDisposable

• The Dispose method is public and can be called at anytime.

• The Dispose method can be safely called mulitple times.

• The destructor calls Dispose()

• The Dispose method calls GC.SuppressFinalize to stop the garbage collector from needlessly calling the destructor. It’s optional!

• All the regular methods of the class check to see whether the object has alreaady been disposed. If it has, they throw an exception.