My last post laid out how the employment of events has changed recently. Most importantly the broadcasting scenario – which was the major pattern so far – is no longer the only relevant pattern. Rather the “event-based asynchronous pattern”, MSDN, has emerged. Reasons include the inherently asynchronous nature of Silverlight as well as parallel patterns.
Now for the practical implications of this new pattern. Let’s look at an example to get the idea, and a better understanding of the consequences in code…
Let’s assume a component that is instantiated, does some work (allegedly asynchronous, and notifying about the progress) and provides the result at the end via an event. This is akin to making a server call, showing a confirmation message box, or the way the BackgroundWorker component works.
Example 1: Using events
First, implementing the a component the classical way would look somewhat like this:
The result event needs a respective EventArgs class, the declaration and the trigger method:
public class WorkResultEventArgs : EventArgs
{
public object ResultData { get; set; }
}
public class MyWorkingComponent1
{
public event EventHandler<WorkResultEventArgs> WorkResult;
protected virtual void OnWorkResult(object resultData)
{
if (WorkResult != null)
WorkResult(this, new WorkResultEventArgs() { ResultData = resultData });
}
…
}
A work progress event should go a little further and provide cancelation support:
public class WorkProgressEventArgs : CancelEventArgs
{
public int Progress { get; set; }
public object SomeData { get; set; }
}
public class MyWorkingComponent1
{
public event EventHandler<WorkProgressEventArgs> WorkProgress;
protected virtual bool OnWorkProgress(int progress, object someData)
{
if (WorkProgress == null)
return true;
var ea = new WorkProgressEventArgs() { Progress = progress, SomeData = someData, Cancel = false };
WorkProgress(this, ea);
return ea.Cancel ? false : true;
}
…
}
Now we only need the actual worker method:
public class MyWorkingComponent1
{
…
public void StartWork()
{
int sum = 0;
for (int i = 0; i < 10; ++i)
{
sum += i;
if (!OnWorkProgress(i, sum))
return;
}
OnWorkResult(sum);
}
}
Again, we may assume that there is some asynchronicity involved, e.g. the loop could contain a web request or something. But this example should do for the sake of the argument.
The usage (as by support form Visual Studio to create the event handlers) would look like this:
public void Test1()
{
var worker = new MyWorkingComponent1();
worker.WorkProgress += new EventHandler<WorkProgressEventArgs>(Worker_WorkProgress);
worker.WorkResult += new EventHandler<WorkResultEventArgs>(Worker_WorkResult);
worker.StartWork();
}
void Worker_WorkProgress(object sender, WorkProgressEventArgs e)
{
Console.WriteLine(e.Progress + ":" + e.SomeData);
}
void Worker_WorkResult(object sender, WorkResultEventArgs e)
{
Console.WriteLine("Result:" + e.ResultData);
}
Creating the component, registering the event handlers, running the task, throw the component away. The fact that events are multi cast capable is never used at all (and never will, as the component is rather short-lived).
I guess we can agree that this is all very boilerplate. And all in all, that’s quite some overhead, from the component perspective as well as from the client code.
Example 2: Using callbacks
Now let’s try the new approach. Rather than defining an event, I pass in two callbacks. The information that was carried in the EventArgs is moved to the parameter lists, thus no need for these classes. The Cancel property is replaced by the return value of the callback. And since the client code always follows the same idiom, I expect the callbacks as constructor parameters, eliminating a source of errors along the way — something that is not possible with event handlers:
public class MyWorkingComponent2
{
public Action<MyWorkingComponent2, object> WorkResult {get;set;}
public Func<MyWorkingComponent2, int, object, bool> WorkProgress { get; set; }
public MyWorkingComponent2(
Action<MyWorkingComponent2, object> workResult,
Func<MyWorkingComponent2, int, object, bool> workProgress)
{
WorkResult = workResult;
WorkProgress= workProgress;
}
…
}
The worker method changes only slightly:
public class MyWorkingComponent2
{
…
public void StartWork()
{
int sum = 0;
for (int i = 0; i < 10; ++i)
{
sum += i;
if (!WorkProgress(this, i, sum))
return;
}
WorkResult(this, sum);
}
}
That’s it. No EventArgs classes, no events, no respective OnEventHappened methods. Granted, the callback declarations are a little more complex, and their parameters also lack intellisense providing information about the semantics of each parameter. But otherwise? Way shorter, way more concise, way less overhead. The actual worker method hasn’t changed at all, but all the event related overhead is gone, which amounted to only 40% LOC.
Now the client code, first only slightly adapted:
public void Test1()
{
var worker = new MyWorkingComponent2(
(sender, resultData) => Worker_WorkResult(sender, resultData),
(sender, progress, someData) => Worker_WorkProgress(sender, progress, someData)
);
worker.StartWork();
}
bool Worker_WorkProgress(object sender, int progress, object someData)
{
Console.WriteLine(progress + ":" + someData);
return true;
}
void Worker_WorkResult(object sender, object resultData)
{
Console.WriteLine("Result:" + resultData);
}
As you can see, it didn’t change that much. But passing in the lambdas via the constructor fits the use case far better than events, and it is even more robust, as I cannot forget to pass in a callback via the constructor, the way I can forget to register an event handler.
Speaking of lambdas, and since the implementation is that simple, we can even simplify the client code further by omitting those two handler methods:
public void Test1()
{
var worker = new MyWorkingComponent2(
(sender, resultData) => { Console.WriteLine("Result:" + resultData); },
(sender, progress, someData) => { Console.WriteLine(progress + ":" + someData); return true; }
);
worker.StartWork();
}
Alright, this would have been possible with events as well if you used anonymous methods. But Visual Studio guides you otherwise and early examples of anonymous methods (before we had lambdas) where rather ugly, so I doubt that can be seen as valid counterargument. Here however lambdas can be seen as typical means of choice.
Verdict
Neat? Net result:
- I’m writing less code on the event source side, including no longer declaring EventArgs classes.
- I’m writing less code on the event sink side.
- The handler methods can use clean parameter lists (rather than EventArgs).
- I’m eliminating the risk of forgetting to register event handlers by making the callbacks explicit parameters.
- I’m elimination the danger of leaks due to failing to consistently deregistering event handlers.
- (That was not addressed in the example, but still.)
- When chaining together several of these steps I can make the logic – especially conditional processing – more explicit and concise.
- Events would either require setting up beforehand (partly unnecessary overhead), or setup on demand, cluttering the handler with registration and deregistration code.
All in all, this is way more readable, way more robust, and way more efficient than using events.
I for one have begun to adopt this scheme quite liberally. My Silverlight bookshelf application has wrappers for service calls that translate the event to callbacks (several actually, including error handling and other demands). My dialogs always take callbacks for OK and Cancel. I so far have two ICommand implementations, both take callbacks (one with parameters, the other without). I even have a PropertyObserver class that translates a PropertyChanged event into a callback.
Actual event handlers? Apart from the wrappers enabling what I just presented, only a few reacting to control events.
In other words: This is not just an interesting technical detail. It really changes the way I’m addressing certain demands.
That’s all for now folks,
AJ.NET
It’s great to see more people adopting this method – it is certainly a lot cleaner in my opinion, and I’ve been using it consistently and with much success since I blogged about it here: http://csharperimage.jeremylikness.com/2009/12/simplifying-asynchronous-calls-in.html. Keep up the great work!
Comment by Jeremy Likness — July 29, 2010 @ 7:29 pm
Thanks for the feedback 🙂
For anyone reading that far: Jeremy’s post is recommended reading. His way of passing the callback to the server call via the userstate has the additional benefit of beeing able to pass in different callbacks. This may come in handy in certain situations, e.g. in paging scenarios where you have to distinguish between first call and subsequent calls. See https://ajdotnet.wordpress.com/2010/03/25/calling-amazon-part-2/ for an example.
Comment by ajdotnet — July 29, 2010 @ 8:24 pm
I like the usage of callbacks/delegates instead of events as well, but it has to be use case dependent. For me the callback approach is just a different pattern with other usage backgrounds than events. Where events follow publish/subscribe, with simple delegates/callbacks you get a point-to-point channel similar to the Command pattern.
Usage of delegates imho is great for asynchronous computation. There should be/has to be exactly one handler handling the result. It would make no sense if there are no handlers and when thinking of synchronous computation there is exactly one handler, too. Only when I think of Event-Based Components (EBCs, see http://www.minddriven.de/index.php/technology/development/event-based-components) I can find some advantages, e.g. connecting logging components to events that log the completion of an async computation. But for the sake of clearness I favor delegates.
I think of passing delegates as of handling dependencies. The delegate is a dependency for a method/class for what happens when an action completes. Thus the variants of dependeny injection apply as well. Such a dependency could be given by constructor-injection or property-injection (while I favor the constructor one), but only if it’s really necessary. In simple async cases I would pass the delegate as dependency directly to the method which is running async. Many times there is no need for a class to know the dependency in a field/property and thus as internal state.
But events have their right to exist as well. From the conceptual side, events are nothing more than multicast delegates and should be used if there are “events” that clients can optionally bind to. Something like “a value has changed… if you like to get noticed about that, attach to the event” is a good example. There could be several subscribers, for the publisher it doesn’t matter. From the technical side I don’t like events in C#. I think that’s one of the reasons why other more powerful and object-oriented approaches like the Reactive Framework have emerged. Event Aggregators are a nice example for decoupled usage of events as well and are a great choice in many situations.
Thus in the whole while I like the intention revealing and explicitness of callbacks, for me it’s depending on the situation which way I would prefer.
Comment by matthiasjauernig — August 6, 2010 @ 11:03 am