DISCLAIMER: The sections about lambda expressions and events are still under construction and not published. I’m working on it. ๐Ÿ˜‰

In this post I’m going to try to explain C# delegates, anonymous function (“lambda expressions”) and events in an (hopefully) easy way.
Since it is necessary to know delegates in order to understand events like Rex M wrote, I’m going to start with delegates.

For someone who has never used similar techniques like delegates or any forms of functional programming before, it is hard to understand the concept, so I’m going to cover as much as possible by analogies and examples. If my explanations are still unclear, please leave a comment and let me know! ๐Ÿ™‚

    Table of contents

  1. Delegates

    1. Basic concepts
    2. Multicasting
  2. Anonymous function
  3. Events

Delegates

First of all: There is nothing you could not do without delegates. Yet they have the capability of sparing a lot of time and many lines of code while making your code more clear and precise (which is, as I have heard, desirable ๐Ÿ˜‰ )
Delegates make it possible to switch between different algorithms easily, they allow dynamic assembling of complex pipelines and they enable you to add functionality to existing methods without having to touch or even know their source code.

But as mentioned above, understanding the concepts is not an easy task, so let’s start slowly (and from the beginning!)

Basic concepts

Since you are reading a post about advanced C#-techniques, you are very likely already experienced about the concepts of object orientated programming.
You know you can declare and define methods that allow you to generalize an operation so it will be possible to repeat it for a whole set of parameter values, like in the following example:

public static void Main(string[] args)
{
    int[] values = new int[3]{1,2,3};
    foreach(int x in values)
    {
        Console.WriteLine(DoubleMe(x));
    }
}
public static int DoubleMe(int x)
{
    return x*2;
}

Instead of doubling each single value “manually”, you can simply define a method which takes a number as the input parameter and use that method for each of your input values. So far, so easy.

What we have done here is generalizing the value while specifying the functionality. But what about the other way around?

Let’s assume we have an input value and a task to perform to that value, but different to the above example, we do not know what the task is.
It might be doubling the value, but it might also be halving or squaring it.
Without delegates, we could specify the desired operation by an identification code and in consequence our code might look like this:

public double Action(int x, int operation)
{
    switch(operation)
    {
    case 0:
        return x * 2;
    case 1:
        return x / 2;
    case 2:
        return x * x;
    }
}

While this example is quite overseeable, you will agree that it is going to become a lot more complicated if the amount of possible actions or the complexity of the actions grow.
Also, if we want to add another option at a later point, we have to expand our code, redo a lot of testing, release a new version, etc. Additionally to that, a switch-case-structure is not exactly what we ment when we said we want to “generalize” the functionality, right? The code is still pretty static, and the level of the abstraction is not comparable to the using of x as an abstract description of an specific value.

Wouldn’t it be nice if we did not have to specify the operation by an numeric parameter? What if we could could pass the operation itself as a parameter?

Well, luckily we can! That is exactly what delegates do.

Delegates allow you to handle methods like you handle objects: You can call them like methods, but you can also pass them as arguments, change and replace them, store them in lists and so on.
Described from the opposite point of view, delegates are objects that can be called as if they were methods.
And here is how it works:

If you want to create custom objects, you first have to create classes to define the objects’ types. Analogously, if you want to create delegate-objects, you first have to declare your delegate.

public delegate int MyAction(int param);

The declaration of a delegate looks similar to a method’s head, but notice the keyword “delegate” between the visibility modifier and the return type.
Also, a delegate declaration ends with a semicolon while there is no method definition.

Once the delegate type has been declared, you can declare delegate references just like you can create references to instances of classes:

MyAction action1;

To enable that instance to actually DO stuff, we have to define it by assigning a it to an instance. Delegate instances are created by the new keyword followed by the delegate type.

action1 = new MyAction(MyMethod);

You may have asked yourself when the actual functionality of the delegate will be defined, since it has not been done at the time of the delegate’s declaration. Well spotted! ๐Ÿ™‚

The delegate type will never have a defined functionality. Instead, all the instances will have their individual functionality, that can be defined by “assigning a method” to it by adding the methods name to the parameter brackets, like it is done in the snippet above.
So you can understand a delegate instance as a “wrapper” object, that refers to one (or more) existing regular method.
Therefore, the delegate type’s return type as well as it’s parameters have to match the delegate declaration.

The next snippet shows the complete declaration and definition of a delegate object:

// delegate declaration
public delegate int Action(int param);

// regular method
public int DoubleMe(int param)
{
    return param * 2;
}

// creation
public static void Main(string[] params)
{
    Action action1 = new Action(DoubleMe);
}

Notice that when the method is assigned to the delegate object (line 13), you must only write the methods name, not it’s parameters or any brackets.

And there you have it: an instance of the type Action, that refers to DoubleMe. It now can be used the same way you can use regular objects: it can be re-assigned, stored in an List and so on.
If you want to call the functionality, you can just call it like it was a method:

action1(42);

And of course, you can also pass it as a parameter:

public delegate int Action(int param);

public int DoubleMe(int param)
{
    return param * 2;
}

public int Operation(int param, Action task)
{
    return task(param);
}

public static void Main(string[] args)
{
    int result = Operation(42, new Action(DoubleMe));
}

Multicasting

One of the nice features of delegates is the possibility of assigning one delegate reference to multiple methods.
The delegate instance has an internal set of methods that will be executed when the delegate is called.

Have a look at the following example:

public delegate void Action();

public void Method1(){ Console.WriteLine("This is #1!"); }
public void Method2(){ Console.WriteLine("This is #2!"); }

public static void Main(string[] args)
{
    Action action1;
    action1 = new Action(Method1);
    action1 += new Action(Method2);

    action1();
}

As you can see, a second instance of Action has been added two the reference using the += operator.
After that, each time action1 is beeing called, both DoubleMe() and SquareMe() will be executed.
So, in this case, the console will show “This is #1! &ltbr&gt This is #2”.

We can remove an assignment from the reference just as easy by using the -/-= operator:

action1 -= Method2;

As you can see, we can use the method’s name directly without any Action(...)-structure around it.
Actually, we can use the same syntax when assigning a method to the delegate. I didn’t do that before, because in my oppinion assining “new objects” to the instance reference helps to make the whole mechanics clearer.
But anyways, you could also do it like that:

public delegate void Action();

public void Method1(){ ... }

public static void Main(string[] args)
{
    Action action1;
    action1 = Method1;
}

Finally, have a look at that last example:

public delegate int Action();

public int Method1(){ return 1; }
public int Method2(){ return 2; }

public static void Main(string[] args)
{
    Action action1;
    action1 = new Action(Method1);
    action1 += new Action(Method2);

    int result = action1();
}

Just like before, two methods were assigned to the delegate.
But in contrast, the methods return integer values.
The return value then is assigned to result.
Obviously, the return values of both methods are conflicting. So what value will result have?
Microsoft’s current implementation returns the return value of the method that has been assigned to the delegate at last.
Hence, in this case, result would be 2.
Yet, it is neither intuitive nor very obvious what the return value will be and this might not be consisten across all current and future implementations of C#.
My recommendation is not to use or rely on structures like that.

Anonymous function

To be added soon…

Events

To be added soon… sorry. ๐Ÿ˜ฆ

Advertisements