.NET Delegates: A C# Bedtime Story

The following is an excerpt from
Windows Forms 2.0 Programming, Chris Sells & Michael Weinhardt,
Addison-Wesley, 2006. It's been updated from the original version for C# 2.0.

Once upon a time, in a strange land south of here, there
was a worker named Peter. He was a diligent worker who would readily accept
requests from his boss. However, his boss was a mean, untrusting man who
insisted on steady progress reports. Since Peter did not want his boss standing
in his office looking over his shoulder, Peter promised to notify his boss
whenever his work progressed. Peter implemented this promise by periodically
calling his boss back via a typed reference like so:

Interfaces

Now Peter was a special person. Not only was he able to
put up with his mean-spirited boss, but he also had a deep connection with the
universe around him. So much so that he felt that the universe was interested in
his progress. Unfortunately, there was no way for Peter to advise the Universe
of his progress unless he added a special Advise method and special callbacks
just for the Universe, in addition to keeping his boss informed. What Peter
really wanted to do was to separate the list of potential notifications from the
implementation of those notification methods. And so he decided to split the
methods into an interface:

Delegates

Unfortunately, Peter was so busy talking his boss into
implementing this interface that he didn't get around to notifying the Universe,
but he knew he would soon. At least he'd abstracted the reference of his boss
far away from him so that others who implemented the IWorkerEvents interface
could be notified of his work progress.

Still, his boss complained bitterly. "Peter!" his boss fumed.
"Why are you bothering to notify me when you start your work or when your work
is progressing?!? I don't care about those events. Not only do you force me to
implement those methods, but you're wasting valuable work time waiting for me to
return from the event, which is further expanded when I am far away! Can't you
figure out a way to stop bothering me?"

And so, Peter decided that while interfaces were useful for many
things, when it came to events, their granularity was not fine enough. He wished
to be able to notify interested parties only of the events that matched their
hearts' desires. So, he decided to break the methods out of the interface into
separate delegate functions, each of which acted like a little tiny interface of
one method each:

Static Listeners

Delegates accomplished the goal of not bothering his
boss with events that he didn't want, but still Peter had not managed to get the
universe on his list of listeners. Since the universe is an all-encompassing
entity, it didn't seem right to hook delegates to instance members (imagine how
many resources multiple instances of the universe would need...). Instead, Peter
need to hook delegates to static members, which delegates support fully:

Events

Unfortunately, the Universe being very busy and
unaccustomed to paying attention to individuals, has managed to replace Peter's
boss's delegate with its own. This is an unintended side effect of making the
delegate fields public in Peter's Worker class. Likewise, if Peter's boss gets
impatient, he can decide to fire Peter's delegates himself (which is just the
kind of rude thing that Peter's boss was apt to do):

Peter wants to make sure that neither of these can happens. He
realizes he needs to add registration and unregistration functions for each
delegate so that listeners can add or remove themselves, but can't clear the
entire list or fire Peter's events. Instead of implementing these functions
himself, Peter uses the event keyword to make the C# compiler build these
methods for him:

Peter knows that the event keyword erects a property around a
delegate, only allowing clients to add or remove themselves (using the += and -=
operators in C#), forcing his boss and the universe to play nicely:

Harvesting All Results

At this point, Peter breathes a sigh of relief. He has
managed to satisfy the requirements of all his listeners without having to be
closely coupled with the specific implementations. However, he notices that
while both his boss and the universe provide grades of his work that he's only
receiving one of the grades. In the face of multiple listeners, he'd really like
to harvest all of their results. So, he reaches into his delegate and pulls out
the list of listeners so that he can call each of them manually:

Unfortunately, since Peter is notifying each listener one at a
time, waiting for each to grade him, these notifications now take up quite a bit
of his time when he should be working. So, he decides to forget the grade and
just fire the event asynchronously:

Asynchronous Notification: Polling

The call to BeginInvoke allows Peter to notify the
listeners while letting Peter get back to work immediately, letting the process
thread pool invoke the delegate. Over time, however, Peter finds that he misses
the feedback on his work. He knows that he does a good job and appreciates the
praise of the universe as a whole (if not his boss specifically). Plus, heís
afraid heís leaking .NET resources acquired by calling BeginInvoke without
calling the corresponding EndInvoke method, so, he fires the event
asynchronously, but polls periodically, looking for the grade to be available:

Asynchronous Notification: Delegates

Unfortunately, Peter is back to what he wanted his boss
to avoid with him in the beginning, i.e. looking over the shoulder of the entity
doing the work. So, he decides to employ his own delegate as a means of
notification when the asynchronous work has completed, allowing him to get back
to work immediately, but still be notified when his work has been graded:

Anonymous Delegates

At this point, Peter is using delegates to notify interested
parties in the process of his work and using delegates to get notified when
grades are available on the work heís completed. The delegates provided by his
boss and the universe are provided by separate entities, so it makes sense that
they are encapsulated in methods on those entities. However, in the case of the
WorkGraded method, thereís really no good reason for this to be a separate
method except the syntactic requirements of C# 1.0. As of C# 2.0, Peter can drop
the code required to handle the processing of his work grade into an
anonymous delegate:

Here, instead of passing in the name of a method to call when
his work has been graded, heís passing in the body of the method itself as
designated with a different use of the delegate keyword to create a method with
no name (and therefore ďanonymousĒ). The body of the method is fundamentally the
same in that Peter still passes the WorkCompleted delegate as a parameter to
BeginInvoke and then pulls it out of AsyncState for use in extracting the
result. However, one of the benefits of anonymous delegates that Peter knows is
that he can make use of the variables in the surrounding context from within the
anonymous delegate body, causing him to rewrite his code thusly:

This code compiles just fine, but when itís run, it will cause
the following exception to be thrown:

System.InvalidOperationException:
The IAsyncResult object provided does not match this delegate.

The problem is that while the wc variable is allowed to be used
in the anonymous delegate, itís still being used by the for-each statement. As
soon as the asynchronous invocation begins, the wc variable changes and the
delegate used to start things (wc) no longer matches the async result passed as
an argument to the anonymous delegate. Peter slaps his head and creates a hybrid
solution:

Happiness in the Universe

Peter, his boss and the universe are finally satisfied.
Peter's boss and the universe are allowed to be notified of the events that
interest them, reducing the burden of implementation and the cost of unnecessary
round-trips. Peter can notify them each, ignoring how long it takes them to
return from their target methods, while still getting his results asynchronously
and handling them using anonymous delegates, resulting in the following complete
solution:

Peter knows that getting results asynchronously comes with
issues, because as soon as he fires events asynchronously, the target methods
are likely to be executed on another thread, as is Peter's notification of when
the target method has completed. However,
Peter is good
friends with Mike, who is very familiar with threading issues and can provide
guidance in that area.