Bracketing Operations With IDisposable
I learned a fun little .NET trick today at work, figured I’d pass it along. This is probably a well-known pattern, but I don’t know its name.
The Problem
Say you have a pair of functions that bracket other operations you want to do. For example, you must first call BeginRender() before you call RenderModel(), and after you’re all done, you need to call EndRender(). Another example would be a EnterLock() on a critical section, that must be accompanied by a LeaveLock(). I’ll call these bracketing functions simply “BeginX” and “EndX” for the purposes of this article.
The problem is that the caller must not only remember to call EndX, but must do so even in case of an exception, to avoid blocking the system.
The Solution: IDisposable
What we’re doing here, really, is acquiring and releasing a resource. The .NET Framework already has a pattern for that, and it’s called IDisposable. Ok, that’s not much of a trick in itself. What I learned today was a little twist on the concept.
The trick is simple:
It’s obvious in retrospect, and it’s clean. The ‘using’ guarantees calling EndX even in case of an exception. And in leveraging IDisposable we can build on top of the C# programmer’s instinct to wrap usages of IDisposable objects in ‘using’ constructs. As a fallback, we can rely on tools like CodeRush to catch it as we type (or FxCop as a fallback).
Implementation
First, we need a fun little helper base class.
public abstract class DisposerBase<T> : IDisposable
{
bool _disposed;
T _owner;
public DisposerBase(T owner)
{ _owner = owner; }
public void Dispose()
{
Debug.Assert(!_disposed);
_disposed = true;
OnDispose(_owner);
}
protected abstract void OnDispose(T owner);
}
Here’s a sample class that demonstrates the IDisposable-bracket pattern. Note the inner class that has access to private methods, which we use to call EndX. This method is private so that the only way to close the bracket is to call Dispose() on the handle. Simple, straightforward, consistent.
public class TransactionManager
{
bool _inTransaction;
public IDisposable BeginTransaction()
{
Debug.Assert(!_inTransaction);
Console.WriteLine("Begin Transaction");
_inTransaction = true;
// ... do other transaction setup here ...
// caller must dispose this when done
return new TransactionDisposer(this);
}
private void EndTransaction(TransactionDisposer state)
{
Debug.Assert(_inTransaction);
Console.WriteLine("End Transaction");
// ... do transaction cleanup here ...
// (state could provide more context if needed)
_inTransaction = false;
}
#region TransactionDisposer
class TransactionDisposer : DisposerBase<TransactionManager>
{
// obviously some state could go here
// (say, for more transactions in flight at once)
public TransactionDisposer(TransactionManager owner)
: base(owner) { }
protected override void OnDispose(TransactionManager owner)
{ owner.EndTransaction(this); }
}
#endregion
public void DoSomeAction(string actionName)
{
Debug.Assert(_inTransaction);
// ... do the action ...
Console.WriteLine(" Action performed: " + actionName);
}
}
Note that this is a very simple example that only permits one transaction in flight at once. If we wanted to do more, we could have a Dictionary that maps outstanding transactions to pending state, or we could just put the state directly into the TransactionDisposer. I’ve noted this in the comments.
Now, it’s a bit of typing to implement this disposer, but that’s what snippets are for, right? Bracketed code like this isn’t overwhelmingly common either, so it shouldn’t be a big deal. Still, I have to say I miss my old friend #define from the C world sometimes. Not often. I’m looking forward to a future version of the C# compiler that lets us plug into the AST directly and codegen as we go.
Anyway, here is an example usage of this pattern:
var transactionManager = new TransactionManager();
using (var handle = transactionManager.BeginTransaction())
{
Page 1 of 2 | Next page
