Delegates and Events
Goals:
- Introduce the concept of a delegate.
- Show how to define a delegate type.
- Show how to register and unregister targets.
- Show how to handle multiple targets.
- Describe the effect of the
event keyword. - Show some common applications of events from the .NET Framework Class Library.
Overview
A delegate is a proxy for a method. Clients interact with the delegate and the delegate
forwards the operation to the method it represents. The advantage of this indirect
arrangement is that the client code is relatively independent of the target method.
The client sees only the delegate and does not need to know much about
the method the delegate represents. Delegates are used in many places throughout the
.NET Framework Class Library. Classic examples are as callbacks in user interface
programming and as the way to specify the work to be performed by a thread.
Background - object-oriented method call
Let's first do a very quick review of a really basic concept. In an object-oriented
system there are two components to a method call: the object and the method. Consider
the Stock class and its Buy method shown below.

We must have an object of the Stock class available in order to call the
Buy method. The object will become the hidden parameter this
inside the method.

Notification
Objects typically store information in their fields. The formal way to say this is
that the object maintains an internal "state". The state changes over time as user
input is received or as client code calls methods. The Student class
shown below models a student at a university and demonstrates the pattern. The
student maintains a numerical rating of their scholarship in the form of a "gpa"
(grade point average). Their gpa changes as they take courses and receive grades.
Each time they finish a class, the RecordClass method will be called
and their grade passed in (an A is worth 4 points, a B worth 3, and so on). A new
value for the gpa will be calculated to take into account the newly received grade.
In the example, we have made the simplifying assumption that each class is worth
one unit.

Various parties might be interested in tracking the student's performance. Certainly
the student's parents come to mind since they might be paying the tuition fees and
want to stay informed of their child's progress. Other likely candidates are the school
registrar, the dean of the college, any honor society for which gpa is a criterion of
membership, etc. The student will be responsible for notifying all interested parties
when the gpa changes. The situation is summarized in the diagram below.

Notification typically involves "registration" and "callback". The targets that would
like to receive notification must register with the caller. The caller will notify
all registered clients whenever the state changes. Another name for this pattern
is "publish/subscribe". In our example, the parents and the registrar would need to
register with the student. The student would notify them at the end of each class
when their gpa is updated. The idea is captured in the figure below.

Delegate definition
.NET uses delegates to implement callbacks. A delegate acts as an intermediary
between the caller and the target. The target creates a delegate and registers it
with the caller. The caller invokes the delegate which forwards the call to the
target. The main advantage to this approach is that the caller and the target do
not communicate directly so the caller never needs to know the type of the target
or the method that it is calling.

The caller and the target do need to know a little bit about each other since the
caller is, in effect, making a method call on the target. The caller and target
need to agree on the type of information that flows between them. In the student
example, it makes sense for the student to send the new value for their gpa to
a target such as a parent. We'll keep things simple and assume the parent doesn't
need to send any information back to the student. If we think of these two pieces
of information in terms of a method signature, we would picture a method that had
an argument of type double (the gpa) and a return type of
void (nothing gets passed back to the student). The information
flowing between the student and their parent is described in the figure below.

For the student and parent to interact, they need a delegate that describes the
information flowing between them. A delegate is defined using the C# keyword
delegate in front of what otherwise looks like a normal method
signature. The name of the delegate goes in the position normally occupied
by the method name. The argument list and return type use the standard
method definition syntax. A delegate for the student/parent interaction is
shown below.

The compiler does a lot of work behind the scenes to process a delegate definition
(typically generating an entire class from the delegate). The result of all the
compiler's work is that a delegate name is actually a type name and we can declare
references of that type and create objects.

Delegate use
Examine the diagram below that shows the links between the student, the delegate, and
the parent.

The student needs to hold a reference to the delegate. To accomplish this, we simply
add a field to the student class of the delegate type.

The parent needs to define a method for the student to call (indirectly through the
delegate of course). The student and parent have agreed that the method should take
a double argument and return void and this interaction was
formalized in the delegate definition. The parent must then define a method that
conforms to the required signature. The parent is free to name the method anything
they like, only the parameter list and return type are constrained.

Next we write the client code that puts everything together. We need to create three
objects: the student, the parent, and the delegate that links them. Creating the
student and the parent are both straightforward operations. Creating the delegate
is much more interesting. When a delegate is created it needs to be given the target
object and the target method since both are necessary to make a method call in an
object-oriented system. The target information is passed to the delegate constructor
and will be stored inside the delegate object. Once the delegate is created, we
register it with the student by assigning to the student's delegate field.

The student needs to call through the delegate whenever their gpa changes. To accomplish
this, we modify the student's RecordClass method to include the invocation.
The call is made using regular method call syntax on the delegate. Internally, the delegate
calls the target method on the target object and passes along the gpa.

Null reference
A delegate is a reference type so delegate fields will default to null.
It is common practice to test delegate fields to make sure they are not null
before attempting to make a callback. The CLR will throw a
NullReferenceException if the call is made when the reference is
null.

Static methods
A static method can be registered using a delegate. When the delegate is created, simply
pass ClassName.MethodName as the constructor argument. Because static methods
do not have a this reference, a target object is needed.

Multiple targets
Multiple targets can be registered with a delegate. Each delegate stores an
"invocation list" of targets and the + and += operators
are used to add new targets to the list. All targets in the list get called
back when the delegate is invoked. The targets will be called in the order they
were added to the invocation list. The student class might use this feature
to notify both their parents whenever their gpa changes. One minor point is worth
mentioning. Notice that += is used to register both targets in the
example below. This is ok even though when the first target is registered the
delegate will be null. The special case is automatically handled
to help make the client code simpler.

The - and -= operators are used to remove a target from
an invocation list.

Events
There are several operations that can be performed on a delegate: assignment,
registering a target with +=, removing a target with -=,
and invocation. The common wisdom says that external code should be able to
register a new target or unregister an existing target and should have no
additional control over a delegate field. We do not want to allow external
code to assign to a delegate because a careless client could accidentally
remove all previously registered targets simply by using = instead
of +=. We do not want to allow invocation because the external
code is not in a position to know when conditions are right for a callback.
The object internally should make this decision whenever its state changes.
So we have a problem. If we make the delegate field public, the client code
gets full control including the power of assignment and invocation. On the
other hand, if we make the delegate field private the client code has no
access at all and cannot even register or unregister targets.

This discussion is reminiscent of the private data / public access method
pattern. The class designer could make the delegate field private and
code public Register and Unregister methods.
This would give the desired interface since clients could only add and
remove targets and would have no additional power. The designers of C#
decided that requiring programmers to constantly code that pattern was
too much busywork. To solve the problem they introduced the event
keyword. Adding event to a public delegate field gives exactly
the desired behavior. External code can still use the += and
-= operators but invocation and direct assignment will not compile.

Applications
The .NET Framework Class Library makes extensive use of events. For example, when
creating a new Thread, the client must pass a ThreadStart
delegate to the constructor. The delegate is used to specify the work that the new
thread should perform. Another common application is in user interface programming.
Controls such as buttons, text boxes, list boxes, etc. offer events. Clients create
delegates and register them with the controls. When the event is triggered, the client
will be called back through the delegate. To supply a concrete example of delegates
and events in use, we will conclude our discussion here by taking a closer look at
a few of the library delegate types and their use with GUI programming.
The .NET Framework Class Library defines a delegate named EventHandler
which is used throughout the library. The EventHandler type is
actually defined in the System namespace which indicates it is
intended to be applicable to many areas of the library. The definition of the
EventHandler delegate is shown below. Note how the delegate
captures the information flowing between a publisher and a subscriber. A publisher
passes itself as the first argument and an EventArgs object as
the second argument. Passing itself to the handler method lets the client code
easily determine which object fired the event. The EventArgs
object is used to pass details of the event.

The data flow through an EventHandler delegate is summarized
in the figure below.

The controls in the System.Windows.Forms namespace use an
EventHandler for their Click event. The event is declared
in the base class Control and is inherited by all derived controls
such as the Button class. The figure below shows the definition of
the event.

Clients that would like to register for the Click event must
define a method with the appropriate signature: a first parameter of type
object, a second parameter of type EventArgs, and
a return type of void. They then create an EventHandler
delegate and use the += operator to register with the event. The
registration process is shown in the figure below. When the user clicks on the
ok button in the GUI, the callback method will be invoked.

The Click event is a good introduction to the topic of GUI event
handling but it is a little too simple to show the full power of the model.
The main reason the Click event is not representative is that
there is generally no need to pass any information from the control to the
event handler for something as simple as a Click. That is,
the only thing the handler needs to know is that the button was clicked and
there is typically no need for more detailed data to be sent. In fact, the
EventArgs class does not even contain any data so the publisher
could not pass any additional information if it wanted to.
Other types of events do need to pass additional information. For example,
a mouse event would likely need to send the coordinates of the mouse click
and the identity of the mouse button the user pressed. To accommodate this
type of interaction there are more specific delegate types that pass derived
types of EventArgs as parameters. The derived types contain
the detailed event information. A delegate definition for a mouse event is
shown in the figure below. Notice how the second parameter is now
MouseEventArgs rather than the more general EventArgs.

The MouseEventArgs class is derived from EventArgs
and adds information specific to mouse events. Part of the class definition
is shown below.

The Control class offers mouse events such as the MouseDown
event shown in the next figure. Notice that the type of the event is MouseEventHandler
rather than the generic EventHandler.

A client that wishes to subscribe to the MouseDown event must code
a method that fits the MouseEventHandler pattern. The process is
shown in the figure below.

There are other interesting events as well such as those for keyboard activity and
painting. They all fit the same basic pattern: a specific delegate type with a
derived class of EventArgs to transmit the event details.