Two Components for your Toolbox
Any desktop application I write from now on will contain these two interfaces. They’re useful enough I thought I should share. Also note, with upcoming improvements in .NET 4.0 (or higher) they might be rendered moot. So far, I don’t think they are, as it’s still difficult to test the code itself that performs asynchronous operations.
First, is an abstraction around the User Interface thread, IUserInterfaceContext. This exists today in the form of SynchronizationContext, but I favor this specific interface because
- It’s more explicit (the SynchronizationContext applies to more than just the main UI thread) whereas this is very clear what its purpose is.
- The API is cleaner – passing a “state” is unnecessary with nested closures.
- It’s easier to grab out of an IoC container. Because a SynchronizationContext is only specific to the context it was created in (which could be a background thread) it’s not meaningful to put a SynchronizationContext argument in your constructor. Which one do you want?
public interface IUserInterfaceContext
{
void Execute(Action action);
void ExecuteAndBlock(Action action);
}
Now, any component in my application can execute code on the UI thread extremely easily. I just register the implementation of IUserInterfaceContext (which does use a SynchronizationContext) when my application is started, which is on the UI thread.
The implementation could look something like this:
public class UserInterfaceContext : IUserInterfaceContext
{
private readonly SynchronizationContext _syncContext;
public UserInterfaceContext(SynchronizationContext syncContext)
{
/* Ensure the SynchronizationContext passed in is the main UI thread context */
_syncContext = syncContext;
}
public void ExecuteAndBlock(Action action)
{
if (_syncContext != null)
{
_syncContext.Send(s => action(), null);
}
else
{
action();
}
}
/// <inheritdoc />
public void Execute(Action action)
{
if (_syncContext != null)
{
_syncContext.Post(s => action(), null);
}
else
{
ThreadPool.QueueUserWorkItem(s => action(), null);
}
}
}
Secondly, is a more generalized example of Jeremy Miller’s ICommandExecutor, which is even more generalized as the Active Object pattern. I named mine “IAsyncExecutor” because it executes any code asynchronously. The advantage with this approach is that it drastically simplifies test activities to be able abstract multithreading (to a point) and allow a test to run single threaded. That is nothing but pure win. We’ve also found that using the interface makes the code read better than using a bunch of BeginInvoke/EndInvoke’s or the ThreadPool, or an async pattern such as “void FooAsync(AsyncCallback callback, object state);
public interface IAsyncExecutor
{
void Execute(Action action);
void Execute(Action action, Action after, bool callbacksOnUIThread);
void Execute(Action action, Action after, Action<Exception> error, bool callbacksOnUIThread);
}
You’ll notice that IAsyncExecutor looks a lot like IUserInterfaceContext, and in fact, it can use it under the covers if the callbacksOnUIThread is true.
Both of these are simple interfaces, with trivial implementations, but you’d be surprised how often I’ve wished I’ve had them in the past. What are some “core” interfaces/services/etc that you *must have* in your toolbox?
September 25th, 2009 at 6:21 pm
Shouldn’t it be BlockAndExecute()? The blocking occurs immediately and finishes after execution. It doesn’t execute and *then* block
September 25th, 2009 at 9:12 pm
I bet you’re just a hoot in a code review
– I tend to prefer similar methods to show up next to each other in documentation and intellisense, but otherwise you’re technically correct