Instruqt: Delegates

Nuts and bolts

A Delegate is a type which represents a function pointer, and as such it enables you to dynamically wire up a method caller to the target method. It literally delegates a method call from the delegate to the target method(s).

A Delegate Type defines the delegate protocol, or method signature of the target methods:

delegate int NumberCruncher(int a, int b);

a Delegate Instance represents a method which matches the protocol and has been associated with the delegate:

NumberCruncher multiplier = MultiplyNumbers;

private int MultiplyNumbers(int a, int b) {
    return a*b;
}

All delegates have multicast capabilities (as they implicitly inherit from System.MulticastDelegate), meaning that multiple target methods can be associated to a single delegate, or more correctly, multiple methods can be associated to a delegate instance. This is achieved by using the += operator:

multiplier += AddNumbers;

private int AddNumbers(int a, int b) {
    return a+b;
}

Invoking the delegate will result in all the associated methods executing in the order in which they were added. If the delegate has a return value, only the return value of the last method will the returned, with all other return values discarded.

Removing a method from the list of target methods is done by using the -= operator. The += and -= operators are compiled to the static Combine and Remove methods of the Delegate class.

Plugin Methods

Delegates are handy for plugging dynamic functionality into another method:

public void PerformANumericDuty(int[] values, NumberCruncher cruncher) {
    //do something here...    

    for(int i = 0; i < values.Length; i++)
        values[i] = cruncher(values[i], 10)
    }
}

Generic Delegates

Delegates may use generic type parameters:

delegate T GenericDelegate<T>(T arg);

GenericDelegate<double> squarer = Square;

private double Square(double a){
    return a*a;
}

Contravariance

Delegates are contravariant, meaning that the delegate type parameters can be more specific than the delegate instance type parameters:

class House : Asset { ... }

delegate void SellHouse(House house);

...

private void SellAsset(Asset asset){ ... }

...

SellHouse houseSeller = SellAsset;

...

houseSeller(new House());

Covariance

Delegates are covariant, meaning that the return type of a delegate type can be less specific than the return type of the delegate instance.

class House : Asset { ... }

delegate Asset GetAsset();

...

GetAsset getMyHouse = GetHouse();

...

private House GetHouse() { ... }

...

Asset myhouse = getMyHouse();