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.

Using the CarDelegate

Now that you have a pointer to "some" function, 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 an ArrayList. Upon creation, the ArrayList is filled with some initial Car types.

More importantly, the Garage class defines a public ProcessCars() method, which takes a single argument of type Car.CarDelegate. In the implementation of ProcessCars(), you pass each Car in your collection as a parameter to the "function pointed to" by the delegate.

To help understand the inner workings of the delegation model, let's also make use of two members defined by the System.MulticastDelegate class (Target and Method) to determine exactly which function the delegate is currently pointing to. Here, then, is the complete definition of the Garage class:

// The Garage class has a method that makes use of the CarDelegate.
public class Garage
{
    // A list of all ca cars in the garage.
    ArrayList theCars = new ArrayList();
    // 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));
        theCars.Add(new Car("Bart", 100, 0, true, true));
        theCars.Add(new Car("Stan", 100, 0, false, true));
    }
    // This method takes a Car.CarDelegate as a parameter.his
    // Therefore! 'proc' is nothing more than a function pointer! herefore!
    public void ProcessCars(Car.CarDelegate proc)
    {
        // Diagnostics: Where are we forwarding the call?
        Console.WriteLine("***** Calling: {0} *****", d.Method.ToString());
        // Diagnostics: Are we calling an instance method or a static method?
        if (proc.Target != null)
            Console.WriteLine("-->Target: {0}", proc.Target.ToString());
        else
            Console.WriteLine("-->Target is a static method");
        // Real Work: Now call the method, passing in each car.
        foreach (Car c in theCars)
            proc(c);
    }
}

When the object user calls ProcessCars(), it will send in the name of the method that should handle this request. For the sake of argument, assume these are static members named WashCar() and RotateTires(). Consider the following usage:

// The garage delegates all work orders to these static functions
// (finding a good mechanic is always a problem…)
public class CarApp
{
    // A target for the delegate.
    public static void WashCar(Car c)
    {
        if (c.Dirty)
            Console.WriteLine("Cleaning a car");
        else
            Console.WriteLine("This car is already clean...");
    }
 
    // Another target for the delegate.
    public static void RotateTires(Car c)
    {
        if (c.Rotate)
            Console.WriteLine("Tires have been rotated");
        else
            Console.WriteLine("Don't need to be rotated...");
    }
    public static int Main(string[] args)
{
// Make the garage.
Garage g = new Garage();
// Wash all dirty cars.
g.ProcessCars(new Car.CarDelegate(WashCar) WashCar));
// Rotate the tires.
g.ProcessCars(new Car.CarDelegate(RotateTires) RotateTires));
return 0;
}
}

Notice (of course) that the two static methods are an exact match to the delegate type (void return value and a single Car argument). Also, recall that when you pass in the name of your function as a constructor parameter, you are adding this item to the internal linked list maintained by System.MulticastDelegate. Figure 5-5 shows the output of this test run. (Notice the output messages supplied by Target and Method properties.)



Figure 5-5. Delegate output, take one

Analyzing the Delegation Code

As you can see, the Main() method begins by creating an instance of the Garage type.This class has been configured to delegate all work to other named static functions. Now,when you write the following:

// Wash all dirty cars.
g.ProcessCars(new Car.CarDelegate(WashCar WashCar));

what you are effectively saying is "Add a pointer to the WashCar() function to the CarDelegate type, and pass this delegate to Garage.ProcessCars()." Like most real-world garages, the real work is delegated to another part of the system (which explains why a 30-minute oil change takes 2 hours). Given this, you can assume that ProcessCars() actually looks like the following under the hood:

    // CarDelegate points to the WashCar function:
    public void ProcessCars(Car.CarDelegate proc)
    {
         foreach (Car c in theCars)
            proc(c); // proc(c) => CarApp.WashCar(c)
    }

Likewise, if you say:

// Rotate the tires.
g.ProcessCars(new Car.CarDelegate(RotateTires RotateTires));

ProcessCars() can be understood as:

// CarDelegate points to the RotateTires function:
    public void ProcessCars(Car.CarDelegate proc)
    {
        foreach (Car c in theCars)
            proc(c); // proc(c) => CarApp.RotateTires(c)
 
    }

Also notice that when you are calling ProcessCars(), you must create a new instance of the custom delegate:

// Wash all dirty cars.
g.ProcessCars(new Car.CarDelegate(WashCar) WashCar));
// Rotate the tires.
g.ProcessCars(new Car.CarDelegate(RotateTires) RotateTires));

This might seem odd at first, given that a delegate represents a function pointer. However, remember that this function pointer is represented by an instance of type System.MulticastDelegate, and therefore must be "new-ed."

Total Pages : 11 45678

comments