Anonymous Delegates or DisposableActions?

May 11, 2007

tags: ,
no comments

One of the most useful features of .NET 2.0 is anonymous delegates. They allow you to create “wrappers” for code which run before and after the code, handle exceptions in it, and decide whether to run it or not. Consider the following method:

public static void WrapCall(Callback callback) { try { callback(); } catch (Exception x) { ExceptionManager.Publish(x); throw; } }

Which will be called like this:

WrapCall( delegate() { DataAccess.UpdateSomething(); EmailSender.SendSomething(); });

This ensures that the exception wrapping and publishing code is handled in one and only one place, and that is the wrap call method. The user does not have to worry about publishing exceptions to log.

Another use of anonymous delegates can be in the following method:

public class CallManager { public static void EnableLoggingForCall(Callback callback) { if (Logger.EnabledForCurrentThread) { callback(); } else { Logger.EnabledForCurrentThread = true; callback(); Logger.EnabledForCurrentThread = false; } } }

This method job is to ensure that the logger is enabled for a specific method execution. We want to make sure that if it is already enabled, we do nothing, but if it is not, we enable it and then turn it back off (since we turned it on it is our responsibility to turn it off). This is how we’ll call it:

CallManager.EnableLoggingForCall( delegate() { new CriticalAction().Run() });

But anonymous delegates do not have the prettiest syntax in the world, they can be a bit hard to grasp at first, and you can expect an issue if you write code like this:

CallManager.EnableLoggingForCall( delegate() { base.Run(); });

Because of the way anonymous delegates are implemented, you can expect a compilation warning about this code. You’ll be happy to know, then, that for this scenario we can find an alternative for delegates.

Meet DisposableActions

In this post Oren Eini describes a pattern called DisposableAction. In a similar manner to anonymous delegates, it allows you to ensure certain code is run both before and after the user’s code. In some scenarios, we can use it instead of delegates. Our second example, implemented with DisposableAction, will be used like this:

using (CallManager.EnableLoggingForCall()) { new CriticalAction().Run(); //base.Run() will also work }

And the implementation:

1 public static IDisposable EnableLoggingForCall() 2 { 3 if (Logger.EnabledForCurrentThread) 4 return new DisposableAction(delegate { }); 5 6 Logger.EnabledForCurrentThread = true; 7 return new DisposableAction( 8 delegate 9 { 10 Logger.EnabledForCurrentThread = false; 11 }); 12 }

So what happened here? As described in Oren’s post, a disposable action runs the action attached to it when the Dispose method is called on it. Since the using statement ensures the object it is using gets disposed, we know that lines 3-6 will run before our critical action executes, and that line 10 will run after it executes. This way we have achieved the same thing as we did with an anonymous delegate, but with a simpler syntax and without strange side-affects.

Note that this pattern can only work for the second case I showed here: needing to run some piece of code before and after a block of code is executed. If we had to catch an exception (as we did in the WrapCall example) or have the option not to run the block of code at all (because we want to get the result from the cache, or whatever) we would have to use anonymous delegates. The code in line 10 has no control on the action execution itself, nor can it know if an exception has occurred there.

Still, I find this a very elegant pattern, and I highly recommend using it instead of delegates, when appropriate.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*