Delegate to Lambda - Part 3
Delegate to Lambda - Part 3

One of the powerful and interesting features of delegate is chaining. Delegate chaining can be used when we want to execute multiple methods if something happens. Chaining is possible in two ways. a) Methods have the same signature (return type and parameters) as delegate, can be combined and b) delegate instances of same type can be combined. '+=' sign is used for chaining.

In the first post of this series, we have seen how to declare a delegate. Let's say we have the following methods compatible with the delegate.

private delegate void MyDelegate();
private static void Foo() // compatible to MyDelegate because the signature matches
{
    Console.WriteLine("Foo();");
}
private static void Goo() // compatible to MyDelegate because the signature matches
{
    Console.WriteLine("Goo();");
}
private static void Soo() // compatible to MyDelegate because the signature matches
{
    Console.WriteLine("Soo();");
}

Now, in our main we want to execute all the compatible methods at one go. This is how we can do it,

MyDelegate deDelegate = Foo;
deDelegate += Goo;
deDelegate += Soo;
deDelegate();

Chaining Delegate

The first line of the example above is self explanatory because in my earlier post I've shown that it's possible to use the method name directly to a delegate instead of creating a new delegate instance. In second and third lines, one by one we are just adding more compatible methods to the the delegate instance using '+='. Using '+=' is just a syntactic suger of how compiler does the actual implementation. And this is our second way of chaining as we mentioned at the begining of the post that "delegate instances of same type can be combined". 

How compiler handles chaining

In the second line compiler figures out that we are trying to add a new method to the same delegate instance where we have already created a reference to 'Foo()'. So, compiler tries to call the 'Delegate.Combine()' method that takes two delegate instances of same type. While passing 'Goo()' as the second parameter, compiler intantiate a new instance of 'MyDelegate()' because at this point 'Goo' is just a name of a compatible method and the 'Delegate.Combine()' method needs a 'Delegate' instance. Finally, compiler casts the entire 'Delegate.Combine()' method to 'MyDelegate', because 'Delegate.Combine()' returns a 'Delegate' type whereas we need assign the value to an instance of 'MyDelegate' type. Example below should help you better to understand the compiler trick:

MyDelegate deDelegate = Foo;
/*won't compile because compiler can't resolve what is Goo*/
//deDelegate = Delegate.Combine(deDelegate, Goo);
 
/*won't compile yet due to casting error*/
//deDelegate = Delegate.Combine(deDelegate, new MyDelegate(Goo));
 
deDelegate = (MyDelegate)Delegate.Combine(deDelegate, new MyDelegate(Goo));
// adds Soo, the same way as Goo
deDelegate = (MyDelegate) Delegate.Combine(deDelegate, new MyDelegate(Soo));
 
deDelegate();

There are many other ways to chain methods programatically instead of using '+=' sign. Below is a way, how We can achieve the same effect as compiler: 

Action chain = new Action(Foo) + Goo + Soo;

Above example uses 'Action' that is nothing but a delegate that always returns void. We'll discuss more about different types of delegates later in this series.

Each delegate returns different value

Now, so far we have seen chaining delegates that returns void. What if we want to chain a bunch of delegate or compatible methods where each returns some value. Something like the following snippet:

public delegate int YourDelegate();
static void Main(string[] args)
{
    YourDelegate del = ReturnTwo; // reference to ReturnTwo()
    del += ReturnThree; // adding ReturnThree()
    var result = del(); // invoke delegate and get the returned value
     
    Console.WriteLine(result);
    Console.ReadLine();
}

The snippet above is a bit tricky. If we think of a method then we know that a method can only return one value or one reference. From the code above it looks like that first we are going to get '2' and then we are adding '3' to it as the return value of 'ReturnThree'. So, the value of 'result' should be '5'. But, it is impossible. Because, first of all we are not calling the methods at all, we are just pointing to them. Methods gets executed when we invoke the delegate. Then, when we are adding another method, we are refering to a delegate that has two methods in its invocation list. When compiler finds that we are trying to chain methods that returns some value then it just executes the last method from the invocation list. This is because, if you think deeply, when we invoke the delegate like 'del()' 'ReturnTwo' should get executed and the value of 'result' should be '2'. Then again 'RturnTree' should executed and the value 'result' should be '3'. So, ultimately we are executing the last method and getting it's value assigned in 'result'.

Workaround

But there is a work around if we want to get the '2 + 3' effect, meaning executing all the methods from the invocation list and return their added value. Here is an example of how we can do it:

YourDelegate del = ReturnTwo;
del += ReturnThree;
//var result = del();
var result = WorkAroundToGetTheAddEffect(del);
Console.WriteLine(result);
Console.ReadLine(); 

Implementation of 'WokrAroundToGetTheAddEffect()':

private static int WorkAroundToGetTheAddEffect(YourDelegate dYourDelegate)
{
    var invokationList = dYourDelegate.GetInvocationList();
    var result = 0;
    foreach (YourDelegate invoker in invokationList)//invoker is casted as YourDelegate
    {
         result += invoker();//invocation is possible due to explicit casting
    }
    return result;
}

To be continued: Delegate To Lambda - Part 4

May 26, 2014
Jahan Sarwar