Delegates (basics)

A delegate is like an enhanced classical pointer. It is type-safe and needs a specific method signature. Delegates can invoke methods, thus they can be used for events.
To declare a delegate simply add the word “delegate” in front of a method definition.

// methods
private double Multiply(double a, double b) { return a * b; }
private double Add(double a, double b) { return a + b; }

// a delegate for any of the above methods
private delegate double dCalc(double a, double b);

Assign a method to a delegate and then use the delegate to call the method.

void Delegates1() {
    dCalc calc = Add; // creates a new delegate
    Console.WriteLine("Sum " + calc(6.0, 3.0));

    calc = Multiply; // creates a new delegate
    Console.WriteLine("Product " + calc(6.0, 3.0));

    Console.ReadLine();
} //

example output:
Sum 9
Product 18

You can use operators to add or remove delegates. The next example shows that a delegate can point to multiple methods. This can be done, because delegates inherit from the System.MulticastDelegate class, which in turn inherits from System.Delegate.

private delegate void dPrint();

private void PrintA() { Console.WriteLine("Print A"); }
private void PrintB() { Console.WriteLine("Print B"); }

void Delegates2() {
    Console.WriteLine("--------------------------------");
    dPrint print = PrintA;
    print += PrintB;
    Console.WriteLine("added A and B");
    print();

    Console.WriteLine("--------------------------------");
    print -= PrintA;
    Console.WriteLine("removed A");
    print();

    Console.ReadLine();
} //

example output:
——————————–
added A and B
Print A
Print B
——————————–
removed A
Print B

Delegates do have iterators. You can loop through and/or count them. By now you should easily understand that delegates have nothing to do with pointers (as eg. used in C++). They do not just point somewhere. A delegate is a complex class. And we are dealing with multicast delegates on top of that.

private delegate void dPrint();

private void PrintA() { Console.WriteLine("Print A"); }
private void PrintB() { Console.WriteLine("Print B"); }

void Delegates3() {
    dPrint print = PrintA;
    print += PrintB;
    print += PrintB;
    print();
    Console.WriteLine("number of invokes: " + print.GetInvocationList().GetLength(0));
    foreach (Delegate d in print.GetInvocationList()) {
        Console.WriteLine(d.Method.Name + "()");
    }

    Console.ReadLine();
} //

example output:
Print A
Print B
Print B
number of invokes: 3
PrintA()
PrintB()
PrintB()

There are some words to learn here:

Covariance describes a return value that is more derived than defined in the delegate.
Contravariance describes a method with parameters that are less derived than those defined in the delegate.
Invariant describes a generic type parameter that is neither marked covariant nor contravariant.
Variance Covariance and Contravariance are collectively called variance.

Covariance looks like standard polymorphism, whereas Contravariance seems counterintuitive. What is important to remember is that a covariant type parameter can be used as the return type of a delegate, and contravariant type parameters can be used as parameter types.

Contravariance was introduced in C# 4. Source code that did not compile in C# 3.5 is now compiling and running very well under C# 4. Contravariance is meaningful for write-only methods. So the input does not produce an output of the same hierarchy.

Covariance example:

class A { public int dummyA = 0; }
class B : A { public double dummyB = 0.0; }

delegate A dCovariance();

B Test() { return new B(); }

void Delegates4() {
    dCovariance covariance = Test;  // test returns class B, not class A as defined in the delegate

    foreach (Delegate d in covariance.GetInvocationList()) {
        Console.WriteLine(d.Method.Name + "()");
    }

    Console.ReadLine();
} //

example output:
Test()

Contravariance example:

class A { public int dummyA = 0; }
class B : A { public double dummyB = 0.0; }

void Test2(A xParameter) { return; }

delegate void dContravariance(B xClassB);

void Delegates5() {
    // The parameter in Test2() is of type class "A", but the
    // delegate was defined with a class "B" parameter.
    dContravariance contravariance = Test2;

    foreach (Delegate d in contravariance.GetInvocationList()) {
        Console.WriteLine(d.Method.Name + "()");
    }

    contravariance(new B());  // <= important !!!!

    Console.ReadLine();
} //

example output:
Test2()

When calling contravariance(…) we have to pass any class B instance as parameter. And the method Test2() has obviously no problems with this. Class B is more derived. It does make sense now that Test2() can be less derived, doesn’t it?

Contravariance will be discussed in more detail when looking at generic interfaces with the “in T” annotation. One commonly used contravariant interface is IComparer. Lean back for the moment and just have a quick look at a custom interface using the “in T” annotation. That should be enough for today.

interface IPerson<in T> {
    string Name { get; set; }
    int Age { get; set; }
}
Advertisements

About Bastian M.K. Ohta

Happiness only real when shared.

Posted on December 16, 2013, in Advanced, Basic, C#, Delegates and tagged , , , , , . Bookmark the permalink. 2 Comments.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: