Background
The Microsoft Visual Studio Start Page displays lists of excellent technical articles written by various authors on all sorts of .NET related topics. Yesterday I noticed an article entitled: “Concurrent Affairs: Implementing the CLR Asynchronous Programming Model” written by Jeffrey Richter. As I glanced through this article I was actually in the middle of wrapping a class for asynchronous access. Since my approach differs somewhat from Mr. Richter’s I decided to write a quick blog entry explaining my method for performing asynchronous programming. Well, the method I use when I want to hide messy implementation details and provide a simple, elegant programming interface.
This approach isn’t always the best, you’ll have to look at your requirements and decide whether my approach meets your needs. Anyway, here is how I usually tackle asynchronous programming.
Introduction
I understand Microsoft likes the whole BeginXXX EndXXX model for asynchronous programming but I think the approach is sometimes too low-level, and occasionally exposes implementation details that should be hidden from callers. I figure a class interface should be designed to address a business problem, and that asynchronous access is just a bunch of technical sugar added into the mix. I guess what I’m saying is that I don’t think public methods should return IAsyncResult – after all, what does IAsyncResult mean from a business perspective? Look at the picturebox control in WinForms. It performs asynchronous operations and hides the details behind a public façade of methods that return nothing (void) and fire events at some later point. That’s my idea of a good class design – at least for components and other “drop-in” objects that should be as simple to use as possible.
I usually begin a design session by addressing business requirements first, then I go back later and add technical details like threading or asynchronous access. That way, I know my design does what it’s supposed to do before I start complicating things. Also, I’ve learned from experience that it’s much easier to test classes when they have simple public interfaces.
An Example
So, as an example, let’s say I start with a simple class like this: class HelloService
{
public string SayHello()
{
return "hello";
}
}
This class provides a basic conversational purpose, and illustrates a class that meets a simple design goal. So, what if we need to say hello asynchronously? Well, we could add a BeginSayHello and EndSayHello method, but in my opinion that would just mess up a perfectly good class interface. Here is how I would add the asynchronous support:
First I would design an event model, with arguments, to support the SayHello method. We need an event to signal to callers when the SayHello method finishes and we need event arguments to return the hello string to that caller. For this example I would start by creating a class like this for the event arguments:
class HelloEventArgs : EventArgs
{
private string data;
public string Data
{
get { return data; }
set { data = value; }
}
public HelloEventArgs(string data)
{
this.data = data;
}
}
After that I would create another class to contain an instance of HelloService and provide any asynchronous support. Using a separate class is a good way to isolate the asynchronous implementation details from the rest of the HelloService class. If you don’t like the additional class then you can always put this code into the original HelloService class. Just remember that my simple example contains a single method – real world business classes are more complicated and would require much more plumbing to provide the asynchronous support. If you don’t get what I’m saying just look at the difference between the class below and the original HelloService class, and then recall that all the additional code in AsyncHelloService exists soley to provide asynchronous support for the SayHello method…
Here is how I would write an asynchronous wrapper class for HelloService:
class AsyncHelloService
{
public event EventHandler AfterHello;
private delegate string SayHelloDelegate();
private HelloService service = new HelloService();
public void AsyncSayHello()
{
SayHelloDelegate d = new SayHelloDelegate(service.SayHello);
d.BeginInvoke(new AsyncCallback(SayHelloHandler), d);
}
protected void OnAfterHello(string hello)
{
if (AfterHello != null)
AfterHello(this, new HelloEventArgs(hello));
}
private void SayHelloHandler(IAsyncResult result)
{
SayHelloDelegate d = result.AsyncState as SayHelloDelegate;
OnAfterHello(d.EndInvoke(result));
}
}
[Begin Rant] First of all, notice that I didn’t make AsyncHelloService inherit from HelloService. It’s tempting to use inheritance, I know, but don’t. Use inheritance when the relationship between two classes is hierarchical in nature – not as a means of attempting code or interface reuse. [End Rant]
OK, the first thing to notice about AsyncHelloService is that it contains a public event named “AfterHello”. That event will be fired whenever the SayHello method finally returns. Listening for that event is how callers will know that their hello message is ready. The delegate (SayHelloDelegate) is used to make the asynchronous call to SayHello in the HelloService object.
The public method AsyncSayHello creates an instance of the SayHelloDelegate delegate and then calls BeginInvoke to dispatch the operation to a background thread. (Don’t worry; .NET will handle the threading for us.) One of the parameters in the call to BeginInvoke is the handler that will be called when the background operation finally completes. In this case we have supplied a method called SayHelloHandler.
The SayHelloHandler method recovers a reference to the delegate that was used to begin the operation, and then calls EndInvoke on that delegate to complete the asynchronous operation and return the original SayHello method’s return string. Once we have that string we need to package it up in an instance of HelloEventArgs and fire the AfterHello event. The OnAfterHello method takes care of that for us.
Using the Example
Using the AsyncHelloService class is actually pretty easy, which was the whole reason for this exercise. Here is an example of a client: class AsyncHelloClient
{
private AsyncHelloService service;
public AsyncHelloClient()
{
service = new AsyncHelloService();
service.AfterHello += new EventHandler(service_AfterHello);
}
public void SayHello()
{
service.AsyncSayHello();
}
void service_AfterHello(object sender, HelloEventArgs e)
{
// TODO : do something with the hello string here.
}
}
The AsyncHelloClient class creates a instance of the AsyncHelloService class, then wires up a handler for the AfterHello event. From there the only thing left to do is call the AsyncSayHello method and wait the service_AfterHello handler to be called. Easy, huh?
Notice that AsyncHelloClient class doesn’t need to mess around with IAsyncResult instances, or delegates, or private handler methods? It can call the SayHello method asyncronously without ever having to know anything about asynchronous anything. The AsyncHelloService class has completely encapsulated all those messy details for us. That’s why I like this approach over the whole BeginXXX/EndXXX method.
Now, like I said at the beginning, this technique isn’t ALWAYS appropriate. You need to look at your requirements and judge for yourself.
That’s my little bit of opinionated blather for today. Ahh, sometimes it’s fun to have a blog!
See ya!