A C# 3.5 pattern – the Accept Block pattern
A coworker pointed me towards this blog post as a useful trick to make code read cleaner. I’ve been using this trick in C# for quite some time, as it’s a very fundamental pattern in Ruby programming. In my head I call this the “Accept Block” pattern, but I’m fairly certain its just a syntactic version of an existing pattern (the Strategy pattern?). In some sense, it’s like being able to write your own version of the “using” or “lock” statements.
Here’s the old imperative way you might write some code:
{
SetupTheDatabase();
bool ShouldExit = SomeFunctionThatNeedsADatabase();
TeardownTheDatabase();
if (ShouldExit)
return;
DoOtherStuff();
}
And here’s how you’d rewrite it using the pattern:
{
SetupTheDatabase();
try {
block()
} finally {
TeardownTheDatabase(); // Note: Make sure this doesn’t throw!
}
}
void Foo()
{
bool ShouldExit;
WithEnsureDatabaseIsSetup(() => {
ShouldExit = SomeFunctionThatNeedsADatabase();
if (ShouldExit)
return;
});
DoOtherStuff();
}
In my opinion, the 2nd version is better to read, and more importantly, allows you to encapsulate the “setup/teardown” pattern – this way, this block of code is consistent, removes copy-paste errors from writing the same thing over and over again, and is easily updated if you want to do additional work when setting up the database.
But there’s a gotcha!
There’s a bug in the above code though that’s quite subtle, and that has bitten me a few times – the syntax () => { } is defining a closure, an anonymous function who can use variables bound inside its lexical environment (to understand the difference, take a look at that ShouldExit variable – even though it’s not a parameter of the function, we were still able to use it). When you call return, in the original example we will exit Foo() – in the new version, we will exit the closure and still execute DoOtherStuff()! Here’s how we should fix it:
{
bool ShouldExit;
WithEnsureDatabaseIsSetup(() => {
ShouldExit = SomeFunctionThatNeedsADatabase();
});
if (ShouldExit)
return; // Now we’ll return properly
DoOtherStuff();
}
Ruby doesn’t have this problem
This is a fundamental pattern in Ruby and built into the language and the runtime, so much so that it has special syntax to make it happen. However in Ruby a “block” is a slightly different syntactic structure than a function, so return will do what you expect:
an_array.each { |item| # This is a block with 1 param
print item
return if item > 2 # Actually returns from Foo, not from block
}
end