Delegates and Events in C#
Richard Wiener, Editor-in-Chief, JOT,
Associate Professor, Department of Computer Science, University of Colorado
at Colorado Springs
|
 |
COLUMN

PDF Version |
Delegates are a feature of C# not found in Java.
A delegate is a class
that encapsulates a method signature. Although it can be used in any
context, it often serves as the basis for the event-handling
model in C# but can be used in a context removed from event handling
(e.g. passing a method to a method through a delegate parameter).
Delegates
provide a type-safe, object-oriented mechanism for treating functions
as objects and passing method references as parameters without
having to use function pointers as in C or C++.
Since delegates are
classes, they have instances (delegate objects). Such delegate instances
contain references to one or more methods.
A delegate instance is connected to one or more methods using a delegate
constructor and later using simple operators. The methods that a delegate
instance is associated with (invocation list) are invoked through the
delegate instance.
Delegates play a central role throughout the .NET
framework and provide the basis for event handling so it is important
to acquire a solid
understanding of how they work and how they can be used.
A delegate
defines the signature (return type and sequence of parameter types)
of the methods that can be added to the invocation list of a
delegate instance. If an attempt is made to add a method to a delegate
object’s
invocation list that does not confirm precisely to the signature in
the delegate class, a compilation error is emitted.
An example of a
delegate class declaration is the following:

Instances of a delegate class are created as indicated in the following
examples:

The invocation list for delegate instance d3 is Method2 and Method3.
An
example of invoking the method(s) on the invocation list of a delegate
would be the following:
String result = d3(16, 4.1, ‘a’);
The return value is always associated with the last method in the
invocation list, Method2 in this case. Often delegates are associated
with methods that return void (commands) so there is no return value
to be concerned about.
Like all classes, delegates can be top-level
classes within a namespace or be nested within an existing class.
The same access modifiers that
can be applied to classes apply for delegates.
Delegates can contain generic parameters or return type.
Listing 1 shows
the use of delegates in a generic sorting application. The generic
features of the soon to be released Version 1.2 of C#
are used in this application. Listing 1 – A
sorting application that shows delegates in action


The delegate class, 
establishes a template for any method that might be used as an instance
of this delegate. Such a method must be generic, with a constrained
generic parameter T that implements IComparable. It must have
two parameters, the first a reference (in/out semantics) to an array
of generic type
T and the second parameter, the size of this array.
Two methods are
defined that satisfy this template: SelectionSort and BubbleSort.
Instances
of the delegate class Sorting are created as follows: 
Any generic parameter could be used in place of type double providing
that its class implements the IComparable interface.
Finally, these delegate instances are invoked as follows:
 Events
and delegates
Event types are used in connection with the observer pattern.
A collection of registered listeners is notified whenever an event
occurs. Objects
that are interested in receiving a notification of an event register
a delegate instance with the event. An event is always defined with
an associated delegate that has been defined and accessible. The
event keyword is a delegate modifier. It must always be used in connection
with a delegate.
An event can be triggered only within the class that declared
it in contrast to a delegate. When the event is
triggered, all delegate instances
registered with the event are invoked. An event has the value null
if it has no registered listeners. To provide an illustration of the
construction and use of events, we consider the following simple
application. In a Stock class
we model
the selling price of a particular stock as a one-dimensional random
walk. From a given starting value, the stock goes either up or
down (by a random value with equal likelihood from 1 to 4 units
on each
move). We wish a notification event to be generated each time the
value of the stock goes 10 units or more above or 10 units or more
below
its starting value. A collection of broker objects must receive
the event notification from the Stock object. We limit
the number of changes for a particular stock to 100.
Listing 2 presents a solution
to this problem.
Listing 2 – Stocks, brokers and events 


In Listing 2, a top-level delegate class, StockNotification, is declared
as follows:

In class Stock, an event is declared as:

A StockNotification instance, Notify, is created in class Broker and
registered with the stockEvent in class Stock as follows:

Whenever the FireEvent method is invoked in class Stock’s ChangeStockValue,
the Notify method in each Broker object is invoked.
It is clear
from the output that each broker object dominates the global stage
of the application during the period that its Notify method is
being sequentially fired by its Stock object. To correct this problem
we utilize threads.
To be continued …
About the author

|
 |
Richard Wiener is
Associate Professor of Computer Science at the University of Colorado
at Colorado Springs. He is also the Editor-in-Chief of JOT and
former Editor-in-Chief of the Journal of Object Oriented Programming.
In addition to University work, Dr. Wiener has authored or co-authored
21 books and works actively as a consultant and software contractor
whenever the possibility arises.
|
Cite this column as follows: Richard Wiener: “Delegates and
Events in C#”, in Journal of Object Technology, vol.
3, no. 5, May-June 2004, pp. 78-85. http://www.jot.fm/issues/issue_2004_05/column8
|