Andrei's ScopeGuard for C#
Posted by Nicholas Blumhardt | July 27, 2007 18:58
Andrei Alexandrescu really opened my eyes to the power of stack unwinding in C++, with scope guards, scoped pointers, scoped mutexes etc. Moving to C# it was really painful not being able to rely on the stack to perform cleanup, and the prospect of try-finally everywhere was depressing.
C#'s using statement fortunately came to the rescue, and IDisposable became 'the way
to go'. If you looked back through my last couple of years' coding, you'd find heaps of little classes
implementing IDisposable for the sole purpose of providing rollback or cleanup in a
using statement. That turns out to be quite a few lines of code...
Well, no more! In the true meta-spirit, I've written the IDisposable to end all
IDisposables!
The little class is called Guard and it uses a stack of delegates called UndoActions
to provide arbitrary undo functionality. An example:
var i = 2;
using (var guard = new Guard())
{
// Set up the guard to revert i if something goes wrong:
var oldI = i;
guard.Add(() => i = oldI);
// Change i:
i = 3;
// Do something risky...
// Then with no exceptions thrown:
guard.Dismiss();
}
In the above code, we make sure that if an exception is thrown inside the using block then
i is restored to its original value.
The Add() method on Guard registers an UndoAction to execute if the
Guard is disposed before it is dismissed. You can add any number of UndoActions to a
guard, and they'll be executed in LIFO order.
You can even initialise a Guard with the UndoAction in its constructor:
using (var guard = new Guard(() => DoCleanup()))
{
...
This is really in many ways a substitute for properly encapsulated resources, but so long as you keep your code DRY by creating resource classes whenever you need to do the same undo action more than once, I think this is excusable :)
Let me know if you find this useful (or even amusing, as it seems to be!)
Files
Comments
Your Comment
Disclaimer: These articles represent the opinions of the authors and may not match the official position of Ubik Systems Pty. Ltd. Confirmation should be sought on all matters involving professional advice.

Posted by Nicholas Blumhardt | July 30, 2007 12:50
I really knocked this article out in a bit of a hurry - I should have mentioned that you'd only really use this where the rollback functionality involved multiple discrete steps (multiple calls to guard.Add()) - otherwise try-finally would be a better choice.