Thursday Night

Paul Betts’s personal website / blog / what-have-you

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:

void Foo()
{
    SetupTheDatabase();
    bool ShouldExit = SomeFunctionThatNeedsADatabase();
    TeardownTheDatabase();

    if (ShouldExit)
        return;

    DoOtherStuff();
}

And here’s how you’d rewrite it using the pattern:

void WithEnsureDatabaseIsSetup(Action block)
{
    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:

void Foo()
{
    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:

def Foo(an_array)
    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

Written by Paul Betts

January 10th, 2010 at 9:25 pm

Posted in Mono / .NET, Ruby