FREE BOOK

Chapter 5: Advanced C# Class Construction Techniques

Posted by Apress Free Book | C# Language January 13, 2009
This chapter rounds out your introduction to the core aspects of the C# language by examining a number of advanced (but extremely useful) syntactic constructs. To begin, you learn how to construct and use an indexer method.

Multicasting

Recall that a multicast delegate is an object that is capable of calling any number of functions. In the current example, you did not make use of this feature. Rather, you made two calls to Garage.ProcessCars(), sending in a new instance of the CarDelegate each time. To illustrate multicasting, assume you have updated Main() to look like the following:

// Add two function pointers to the internal linked list.
public static int Main(string[] args)
{
// Make the garage.
Garage g = new Garage();
// Create two new delegates.
Car.CarDelegate wash = new Car.CarDelegate(WashCar);
Car.CarDelegate rotate = new Car.CarDelegate(RotateTires);
// The over overloaded + operator can be applied to multicast delegates.
loaded // The result is a new delegate that maintains pointers to
// both functions.
g.ProcessCars(wash + rotate rotate);
return 0;
}

Here, you begin by creating two new CarDelegate objects, each of which points to a given function. When you call ProcessCars(), you are actually passing in a new delegate, which holds each function pointer within the internal linked list (crazy huh?). Do note that the + operator is simply a shorthand for calling the static Delegate.Combine() method. Thus, you could write the following equivalent (but uglier) code:

// The + operator has the same effect as calling the Combine() method.
g.ProcessCars((Car.CarDelegate)Delegate.Combine(wash, rotate));

Furthermore, if you wish to hang on to the new delegate for later use, you could write
the following instead:

// Create two new delegates.
Car.CarDelegate wash = new Car.CarDelegate(WashCar);
Car.CarDelegate rotate = new Car.CarDelegate(RotateTires);
// Store the new delegate for later use.
MulticastDelegate d = wash + rotate;
// Send the new delegate into the ProcessCars() method.
g.ProcessCars((Car.CarDelegate)d (d);

Regardless of how you configure a multicast delegate, understand that when you call Combine() (or use the overloaded + operator) you are adding a new function pointer to the internal list. If you wish to remove an item from this internal linked list, you can call the static Remove() method. The first parameter marks the delegate you wish to manipulate, while the second parameter marks the item to remove:

// The static Remove() method returns a Delegate type.
Delegate washOnly = MulticastDelegate.Remove Remove(d, rotate);
g.ProcessCars((Car.CarDelegate)washOnly);

Before you view the output of this program, let's also update ProcessCars() to print out each function pointer stored in the linked list using Delegate.GetInvocationList(). This method returns an array of Delegate objects, which you iterate over using foreach:

// Now print out each member in the linked list.
public void ProcessCarsCar.(CarDelegate proc)
{
// Where are we passing the call?
foreach(Delegate d in proc.GetInvocationList GetInvocationList())
{
Console.WriteLine("***** Calling: " +
d.Method.ToString() + " *****");
}
}

The output is shown in Figure 5-6.



Figure 5-6. Delegate output, take two

Instance Methods as Callbacks


Currently, the CarDelegate type is storing pointers to static functions. This is not a requirement of the delegate protocol. It is also possible to delegate a call to a method defined on any object instance. To illustrate, assume that the WashCar() and RotateTires() methods have now been moved into a new class named ServiceDept:

// We have now moved the static functions into a helper class.
public class ServiceDept
{
    // Not static!
    public void WashCar(Car c)
    {
        if (c.Dirty)
            Console.WriteLine("Cleaning a car");
        else
            Console.WriteLine("This car is already clean...");
    }
    // Still not static!
    public void RotateTires(Car c)
    {
        if (c.Rotate)
            Console.WriteLine("Tires have been rotated");
        else
            Console.WriteLine("Don't need to be rotated...");
    }
}

You could now update Main() as so:

// Delegate to instance methods of the ServiceDept type.
public static int Main(string[] args)
{
// Make the garage garage.
Garage g = new Garage();
// Make the service department.
ServiceDept sd = new ServiceDept();
// The garage delegates the work to the service department.
Car.CarDelegate wash = new Car.CarDelegate(sd.WashCar WashCar);
Car.CarDelegate rotate = new Car.CarDelegate(sd.RotateTires RotateTires);
MulticastDelegate d = wash + rotate;
// Tell the garage to do some work.
g.ProcessCars((Car.CarDelegate)d);
return 0;
}

Now notice the output in Figure 5-7 (check out the name of the target).



Figure 5-7. Delegating to instance methods

SOURCE CODE The CarDelegate project is located under the Chapter 5 subdirectory.

Total Pages : 11 56789

comments