Previous column

Next article

Delegates and Events in C#

Richard Wiener, Editor-in-Chief, JOT, Associate Professor, Department of Computer Science, University of Colorado at Colorado Springs

space COLUMN

PDF Icon
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


space 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.

Previous column

Next article