Monday, June 16, 2008

Using IDisposable Types

Instances of types implementing the IDisposable interface need to be handled very carefully, especially in the presence of possible exceptions. For example, the following code may actually result in the resource not getting disposed
MessageQueue queue = new MessageQueue(“server\\queueName”);
queue.Send(“Hello World”);
queue.Dispose();

If the Send operation throws an exception, the call to Dispose on the following line will not get executed.
A better way to implement the code is to dispose resources in a finally block, which always gets executed, even when an exception is thrown.
MessageQueue queue;
try{
queue = new MessageQueue(“server\\queueName”);
queue.Send(“Hello World”);
}
finally{
queue.Dispose()
}

The codes become more robust but also a bit less readable. The readability problem is even more pronounced in the case of two resources that need to be disposed, since such scenarios require nested try-finally blocks.
MessageQueue source;
MessageQueue destination;
try{
source = new MessageQueue(“server\\sourceQueue”);
Message message = source.Receive();
try{
destination = new MessageQueue(“server\\destinationQueue”);
destination.Send(message);
}
finally{
destination.Dispose();
}
}
finally{
source.Dispose();
}

Unfortunately, the code above lost a lot of its original simplicity. This is why some languages, including C#, introduced the using statement. The statement has semantics similar to the code above without sacrificing simplicity. It expands to multiple try-finally blocks and automatically calls Dispose() on IDisposable objects created inside the using clause.
using(
MessageQueue source = new MessageQueue(“server\\sourceQueue”),
destination = new MessageQueue(“server\\destinationQueue”)
){
Message message = source.Receive();
destination.Send(message);
}

If you work with languages that don’t provide support similar to like the using statement, you should implement the fully expanded code manually.

No comments: