Using, Lambda, and RAII
One of the things like most about C++ for production coding is RAII. I can make an object on the stack clean up resources by putting the cleanup code in the object’s destructor. When the function/method goes out of scope, the destructor is called and then resources get cleaned up. It’s a nice way to prevent memory leaks, for one.
C# doesn’t have this. The closest thing to it is try/finally, and I don’t like that as a solution. You have inevitably write code like this:
SomeResource r = null;
SomeOtherResource k = null;
try {
// code for resources;
}
finally {
if (r != null) r.Dispose();
r = null;
if (k != null) k.Dispose();
k = null;
}
This feels clumsy and is largely due to the try block and the finally block having completely different scopes. I;d prefer that they shared the same scope, even though it violates POLA. Maybe if finally was set off with -{and }- it would signify a continuation of the previous scope. Hell, catch should have that option too.
The real cool thing is that C# already has syntactic sugar to do something like this in the using() { } block, so we can use that to clean up. Let’s start with a common task as a problem. I’m working with a Stream and I want to do some processing on it which may fail. My code insists that the stream position get restored on the way out. So I’ll start off with this code:
void Process(Stream stm)
{
long savePos = stm.Position;
// do work
stm.Seek(savePos, SeekOrigin.Begin);
}
Ok, that works. But as the do work section grows, it can be easier to forget about that Seek at the end, and hey – what happens if you return or throw. So you learn and refactor your code to look like this:
void Process(Stream stm)
{
long savePos = stm.Position;
try {
// do work
}
finally {
stm.Seek(savePos, SeekOrigin.Begin);
}
}
That’s better. It’s more resilient and does what we want, but I still don’t like it. I don’t like that savePos’s declaration, assignment, and use is so badly located. Further, since save pos is a normal local variable, it can get written over – maybe not intentionally.
So now let’s use using to help us out. To do this, we’re going to start with a generic helper class that implements IDisposable (see my earlier blog IDisposable Made E-Z):
public class ResourceReleaser<T> : IDisposable
{
private Action<T> _action;
private bool _disposed;
private T _val;
public ResourceReleaser(T val, Action<T> action)
{
_action = action;
_val = val;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public ~ResourceReleaser()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_disposed = true;
_action(_val);
}
}
}
this now lets me implement the same stream code like this:
using(ResourceReleaser<long> r = new ResourceReleaser<long>(stm.Position, pos => stm.Seek(pos, SeekOrigin.Begin)) {
// do work
}
Basically, resource released will encapsulate a single generic value at construction that it will pass to its Action delegate at disposal. This will restore the stream position as expected. I still don’t like it as there is a lot of cruft before the meat. I can get around this to a certain extent by writing the following code:
public class StreamPositionRestorer : ResourceReleaser<long>
{
public StreamPositionRestorer(Stream s) : base(s.Position, x => s.Seek(x)) { }
}
which defines a special purpose class to do the work we want. Now my using block looks like this:
using (StreamPositionRestorer s = new StreamPositionRestorer(stm)) {
// do work
}
and now I’ve got a chunk of code with which I’m happy. Well, almost. There is one other problem that can come up. I can shoot myself in the foot badly by creating a StreamPositionRestorer outside of the using block. If I do that, then the Stream will have its position changed at some indeterminate point in the future, courtesy of the garbage collector. So, given that’s in issue, I really want this:
long savePos = stm.Position;
using (() => stm.Seek(savePos)) {
// do work
}
so now the using block takes either an expression of type IDisposable or an Action and the action is executed when scope is done. Written in C# without using, it would look like this:
long savePos = stm.Position;
Action action = () => stm.Seek(savePos, SeekOrigin.Begin);
try {
// do work
}
finally {
action();
}
note that I’ve taken the generic parameter away. It doesn’t help us since it will be evaluated at action execution time and we need early binding. It still has the problem of savePos being writable in the lifetime of the try block. So how about it Anders? using with lambdas?