
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
352 CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS
ldarg.0 dup
ldfld class CarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList ldarg.1
call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine( class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) castclass CarDelegate.Car/AboutToBlow
stfld class CarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList ret
}
The Delegate class also defines a static Remove() method that allows a caller to dynamically remove a member from the invocation list. C# developers can leverage the overloaded -= operator as a shorthand notation.
■Note Be aware that the object passed into Remove() must match the associated signature, but not necessarily the actual delegate instance you wish to remove. In other words, you are not actually required to hold on to the exact delegate object you added to the collection in order to precisely remove that instance; you can create a new delegate object pointing to the same function on the same instance and pass it to Remove(). The delegate will match the previous delegate referencing the same objects.
If you wish to allow the caller the option to detach from the AboutToBlow and Exploded notifications, you could add the following additional helper methods to the Car type (note the -= operators at work):
public class Car
{
// Remove member from the invocation lists.
public void RemoveAboutToBlow(AboutToBlow clientMethod) { almostDeadList -= clientMethod; }
public void RemoveExploded(Exploded clientMethod) { explodedList -= clientMethod; }
...
}
Again, the -= syntax is simply a shorthand notation for manually calling the static Delegate. Remove() method, as illustrated by the following CIL code for the RemoveAboutToBlow() member of the Car type:
.method public hidebysig instance void RemoveAboutToBlow(class CarDelegate.Car/AboutToBlow clientMethod) cil managed
{
.maxstack 8 ldarg.0
dup
ldfld class CarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList ldarg.1
call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove( class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) castclass CarDelegate.Car/AboutToBlow

CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS |
353 |
stfld class CarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList ret
}
Removing a Target from a Delegate’s Invocation List
With the current updates to the Car class, we could stop receiving the Exploded notification by updating Main() as follows:
static void Main(string[] args)
{
Console.WriteLine("***** Delegates as event enablers *****\n");
Car c1 = new Car("SlugBug", 100, 10);
// Hold onto Car.Exploded delegate object for later use.
Car.Exploded d = new Car.Exploded(CarExploded); c1.OnExploded(d);
...
//Remove CarExploded method
//from invocation list. c1.RemoveExploded(d);
...
}
The output of our CarDelegate application can be seen in Figure 11-4.
Figure 11-4. The CarDelegate application at work
■Source Code The CarDelegate project is located under the Chapter 11 subdirectory.
A More Elaborate Delegate Example
To illustrate a more advanced use of delegates, create a new Console Application named CarGarage (be sure to include your Car/Radio type definitions into this new project). Let’s begin by updating the Car class to include two new Boolean member variables. The first is used to determine whether the automobile is due for a wash (isDirty); the other represents whether the car in question is in need of a tire rotation (shouldRotate). To enable the object user to interact with this new state data, Car also defines some additional properties and an updated constructor. Here is the story so far:

354 CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS
// Updated Car class. public class Car
{
...
//Are we in need of a wash? Need to rotate tires? private bool isDirty;
private bool shouldRotate;
//Extra params to set bools.
public Car(string name, int max, int curr, bool washCar, bool rotateTires)
{
...
isDirty = washCar; shouldRotate = rotateTires;
}
public bool Dirty
{
get{ return isDirty; } set{ isDirty = value; }
}
public bool Rotate
{
get{ return shouldRotate; } set{ shouldRotate = value; }
}
}
Now, also assume the Car type nests a new delegate type named CarMaintenanceDelegate:
// Car defines yet another delegate. public class Car
{
//Can call any method taking a Car as
//a parameter and returning nothing.
public delegate void CarMaintenanceDelegate (Car c);
...
}
Notice that the CarMaintenanceDelegate type can point to any function taking a Car as a parameter and returns nothing.
Delegates As Parameters
Now that you have a new delegate type that points to methods taking a Car parameter and returning nothing, you can create other functions that take this delegate as a parameter. To illustrate, assume you have a new class named Garage. This type maintains a collection of Car types contained in a List<T>. Upon creation, the List<T> is filled with some initial Car types (be sure to import the System.Collections.Generic namespace into your new code file):
// The Garage class maintains a list of Car types. public class Garage
{
// A list of all cars in the garage. private List<Car> theCars = new List<Car>();

CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS |
355 |
// Create the cars in the garage. public Garage()
{
// Recall, we updated the ctor to set isDirty and shouldRotate. theCars.Add(new Car("Viper", 100, 0, true, false)); theCars.Add(new Car("Fred", 100, 0, false, false)); theCars.Add(new Car("BillyBob", 100, 0, false, true));
}
}
The Garage class also defines a public ProcessCars() method, which takes a single argument of our new delegate type (Car.CarMaintenanceDelegate). In the implementation of ProcessCars(), you pass each Car in your collection as a parameter to the “function pointed to” by the delegate.
ProcessCars() also makes use of the Target and Method members of System.MulticastDelegate to determine exactly which function the delegate is currently pointing to:
// The Garage class has a method that makes use of the CarMaintenanceDelegate. public class Garage
{
...
public void ProcessCars(Car.CarMaintenanceDelegate proc)
{
//Where are we forwarding the call?
Console.WriteLine("***** Calling: {0} *****",
proc.Method);
//Are we calling an instance method or a static method? if(proc.Target != null)
Console.WriteLine("-->Target: {0} ", proc.Target); else
Console.WriteLine("-->Target is a static method");
//Call the method "pointed to," passing in each car. foreach (Car c in theCars)
{
Console.WriteLine("\n-> Processing a Car"); proc(c);
}
}
}
Like any delegate operation, when calling ProcessCars(), we send in the address of the method that should handle this request (via a delegate type). Recall that a delegate may point to either static or instance-level methods. For the sake of argument, assume these are instance members named WashCar() and RotateTires() that are defined by a new class named ServiceDepartment. Notice that these two methods are making use of the new Rotate and Dirty properties of the Car type.
//This class defines method to be invoked by
//the Car.CarMaintenanceDelegate type. public class ServiceDepartment
{
public void WashCar(Car c)
{
if(c.Dirty) Console.WriteLine("Cleaning a car");
else
Console.WriteLine("This car is already clean...");
}

356 CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS
public void RotateTires(Car c)
{
if(c.Rotate)
Console.WriteLine("Tires have been rotated"); else
Console.WriteLine("Don't need to be rotated...");
}
}
Now, to illustrate the interplay between the new Car.CarMaintenanceDelegate, Garage, and ServiceDepartment types, consider the following usage:
//The Garage delegates all work orders to the ServiceDepartment
//(finding a good mechanic is always a problem...)
static void Main(string[] args)
{
Console.WriteLine("*****Delegates as Parameters *****\n");
//Make the garage.
Garage g = new Garage();
//Make the service department.
ServiceDepartment sd = new ServiceDepartment();
//Wash all dirty cars.
g.ProcessCars(new Car.CarMaintenanceDelegate(sd.WashCar));
// Rotate the tires.
g.ProcessCars(new Car.CarMaintenanceDelegate(sd.RotateTires)); Console.ReadLine();
}
Figure 11-5 shows the current output.
Figure 11-5. Passing the buck

CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS |
357 |
Analyzing the Delegation Code
The Main() method begins by creating an instance of the Garage and ServiceDepartment types. Now, when you write the following:
// Wash all dirty cars.
g.ProcessCars(new Car.CarMaintenanceDelegate(sd.WashCar));
what you are effectively saying is, “Insert the address of the ServiceDepartment.WashCar() method to a Car.CarMaintenanceDelegate object, and pass this object to Garage.ProcessCars().” Like most real-world garages, the real work is delegated to the service department (which explains why a 30-minute oil change takes 2 hours). Given this, ProcessCars() can be understood as
// CarDelegate points to the ServiceDepartment.WashCar function. public void ProcessCars(Car.CarMaintenanceDelegate proc)
{
...
foreach(Car c in theCars)
proc(c); // proc(c) => ServiceDepartment.WashCar(c)
}
Likewise, if you say the following:
// Rotate the tires.
g.ProcessCars(new Car.CarMaintenanceDelegate(sd.RotateTires));
then ProcessCars() can be understood as
// CarDelegate points to the ServiceDepartment.RotateTires function: public void ProcessCars(Car.CarMaintenanceDelegate proc)
{
foreach(Car c in theCars)
proc(c); // proc(c) => ServiceDepartment.RotateTires(c)
...
}
■Source Code The CarGarage project is located under the Chapter 11 subdirectory.
So at this point in the chapter, you have seen three examples of delegates at work. The first example (SimpleDelegate) illustrated the basics of defining and manipulating delegate types. As shown, a delegate simply maintains a list of methods to call at a later time when you call (implicitly or explicitly) the Invoke() method. Our second example (CarDelegate) illustrated how delegates can be used to establish an event architecture (in place of .NET interface types). Finally, the example we just completed pointed out that delegates can be used as method parameters just like any other
.NET type.
■Note As you explore the .NET collection types, you will notice that delegates are commonly used as parameters to provide sorting and filtering of subitems.
Collectively, these examples have illustrated the core nuts and bolts of the delegate type. To deepen your understanding of this programming construct, let’s now address the role of covariance.

358 CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS
Understanding Delegate Covariance
As you may have noticed, each of the delegates created thus far point to methods returning simple numerical data types (or void). However, assume you have a new Console Application named DelegateCovariance that defines a delegate that can point to methods returning a custom class type (be sure to include your Car/Radio type definitions into this new project):
// Define a delegate pointing to methods that return Car types. public delegate Car ObtainCarDelegate();
Of course, you would be able to define a target for the delegate as expected:
class Program
{
public delegate Car ObtainCarDelegate();
public static Car GetBasicCar()
{ return new Car("Zippy", 150, 50, false, false); }
static void Main(string[] args)
{
ObtainCarDelegate targetA = new ObtainCarDelegate(GetBasicCar); Car c = targetA();
Console.WriteLine("Obtained a {0}", c); Console.ReadLine();
}
}
Now, what if you were to derive a new class from the Car type named SportsCar and wish to create a delegate type that can point to methods returning this class type? Prior to .NET 2.0, you would be required to define an entirely new delegate to do so, given that delegates were so type safe that they did not honor the basic laws of inheritance:
// A new delegate pointing to targets returning SportsCar types. public delegate SportsCar ObtainSportsCarDelegate();
As we now have two delegate types, we now must create an instance of each to obtain Car and
SportsCar types:
class Program
{
public delegate Car ObtainCarDelegate();
public delegate SportsCar ObtainSportsCarDelegate();
public static Car GetBasicCar() { return new Car(); }
public static SportsCar GetSportsCar() { return new SportsCar(); }
static void Main(string[] args)
{
ObtainCarDelegate targetA = new ObtainCarDelegate(GetBasicCar); Car c = targetA();
Console.WriteLine("Obtained a {0}", c);
ObtainSportsCarDelegate targetB =
new ObtainSportsCarDelegate(GetSportsCar); SportsCar sc = targetB();

CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS |
359 |
Console.WriteLine("Obtained a {0}", sc); Console.ReadLine();
}
}
Given the laws of classic inheritance, it would be ideal to build a single delegate type that can point to methods returning either Car or SportsCar objects (after all, a SportsCar “is-a” Car). Covariance (which also goes by the term relaxed delegates) allows for this very possibility. Simply put, covariance allows you to build a single delegate that can point to methods returning class types related by classical inheritance:
class Program
{
//Define a single delegate that may return a Car
//or SportsCar.
public delegate Car ObtainVehicalDelegate();
public static Car GetBasicCar() { return new Car(); }
public static SportsCar GetSportsCar() { return new SportsCar(); }
static void Main(string[] args)
{
Console.WriteLine("***** Delegate Covariance *****\n");
ObtainVehicalDelegate targetA = new ObtainVehicalDelegate(GetBasicCar); Car c = targetA();
Console.WriteLine("Obtained a {0}", c);
// Covariance allows this target assignment.
ObtainVehicalDelegate targetB = new ObtainVehicalDelegate(GetSportsCar); SportsCar sc = (SportsCar)targetB();
Console.WriteLine("Obtained a {0}", sc); Console.ReadLine();
}
}
Notice that the ObtainVehicalDelegate delegate type has been defined to point to methods returning a strongly typed Car type. Given covariance, however, we can point to methods returning derived types as well. To obtain access to the members of the derived type, simply perform an explicit cast.
■Note In a similar vein, contravariance allows you to create a single delegate that can point to numerous methods that receive objects related by classical inheritance. Consult the .NET Framework 3.5 SDK documentation for further details.
■Source Code The DelegateCovariance project is located under the Chapter 11 subdirectory.

360 CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS
Creating Generic Delegates
Recall from the previous chapter that C# does allow you to define generic delegate types. For example, assume you wish to define a delegate that can call any method returning void and receiving a single argument. If the argument in question may differ, you could model this using a type parameter. To illustrate, consider the following code within a new Console Application named GenericDelegate:
namespace GenericDelegate
{
//This generic delegate can call any method
//returning void and taking a single parameter. public delegate void MyGenericDelegate<T>(T arg);
class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Generic Delegates *****\n");
// Register targets.
MyGenericDelegate<string> strTarget =
new MyGenericDelegate<string>(StringTarget); strTarget("Some string data");
MyGenericDelegate<int> intTarget =
new MyGenericDelegate<int>(IntTarget); intTarget(9);
Console.ReadLine();
}
static void StringTarget(string arg)
{
Console.WriteLine("arg in uppercase is: {0}", arg.ToUpper());
}
static void IntTarget(int arg)
{
Console.WriteLine("++arg is: {0}", ++arg);
}
}
}
Notice that MyGenericDelegate<T> defines a single type parameter that represents the argument to pass to the delegate target. When creating an instance of this type, you are required to specify the value of the type parameter as well as the name of the method the delegate will invoke. Thus, if you specified a string type, you send a string value to the target method:
//Create an instance of MyGenericDelegate<T>
//with string as the type parameter.
MyGenericDelegate<string> strTarget =
new MyGenericDelegate<string>(StringTarget); strTarget("Some string data");
Given the format of the strTarget object, the StringTarget() method must now take a single string as a parameter:

CHAPTER 11 ■ DELEGATES, EVENTS, AND LAMBDAS |
361 |
static void StringTarget(string arg)
{
Console.WriteLine("arg in uppercase is: {0}", arg.ToUpper());
}
Simulating Generic Delegates Without Generics
Generic delegates offer a more flexible way to specify the method to be invoked in a type-safe manner. Prior to the introduction to generics (with the release of .NET 2.0), you could achieve a similar end result using a generic System.Object:
public delegate void MyDelegate(object arg);
Although this allows you to send any type of data to a delegate target, you do so without type safety and with possible boxing penalties. For instance, assume you have created two instances of MyDelegate, both of which point to the same method, MyTarget. Note the boxing/unboxing penalties as well as the inherent lack of type safety:
class Program
{
static void Main(string[] args)
{
...
//Register target with "traditional" delegate syntax.
MyDelegate d = new MyDelegate(MyTarget); d("More string data");
//Method group conversion syntax (explained later in this chapter)
MyDelegate d2 = MyTarget; d2(9); // Boxing penalty.
Console.ReadLine();
}
//Due to a lack of type safety, we must
//determine the underlying type before casting. static void MyTarget(object arg)
{
if(arg is int)
{
int i = (int)arg; // Unboxing penalty.
Console.WriteLine("++arg is: {0}", ++i);
}
if(arg is string)
{
string s = (string)arg;
Console.WriteLine("arg in uppercase is: {0}", s.ToUpper());
}
}
}
When you send out a value type to the target site, the value is boxed and unboxed once it is received by the target method. As well, given that the incoming parameter could be anything at all, you must dynamically check the underlying type before casting. Using generic delegates, you can still obtain the desired flexibility without the “issues.”