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 functions
  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 functions

Anonymous functions, or “Lambda-expressions” after the lambda calculus by Alonzo Church, are a way of defining functionality without having to declare a method (“anonymous”: No declaration, no name).
Instead, the functionality is specified right at the moment of the assignment of a delegate.
Therefore, the anonymous function cannot be reused at another point without rewriting it.

Don’t worry, it sounds a lot more complicated than it is.
Let’s explain how it works using an example:

Imagine you have a tuple of two integers and, depending on your particular operation, you want a method to choose one of them (or one in between), but you don’t know what the criterion will be.
Again, like above, you could do so by declaring a long switch/case-structure that chooses the right branch depending on the value of a second parameter.
But now we do know delegates and we could create a Choose-Method that takes a delegate object as parameter, such as in the following snippet:

public delegate int Action(Tuple<int, int> t);

public int Maximum(Tuple<int, int> t)
{
    return Math.Max(t.Item1, t.Item2);
}

private int Choose(Action action, Tuple<int, int> tuple)
{
    return action(tuple);
}

public void MyRandomOperation(Tuple<int, int> t)
{
    Choose(Maximum, t);
}

To be honest, the snippet above would not make much sense in “real” code, because it causes much more overhead than gain. This is meant to be a “minimal example”. Please do not write that many lines of code if it is not necessary! πŸ˜‰
(This reminds me of the “Hello World” Enterprise Edition.)

However, I think it might be a good example to explain anonymous expressions especially because of that ridiculous amount of overhead.

Have a look at the Maximum-method. It’s basically a wrapper for Math.Maxthat matches the delegate’s (Action) parameter signature which allows the passing of that functionality as a parameter to Choose. But by doing it that way, for every puny piece of functionality that might be needed only once, we have to define a method.

In the next snippet, I changed the code above so it now uses anonymous expressions:

public delegate int Action(Tuple<int, int> t);

private int Choose(Action action, Tuple<int, int> tuple)
{
    return action(tuple);
}

public void MyRandomOperation(Tuple<int, int> t)
{
    Choose(x => Math.Max(x.Item1, x.Item2), t);
}

Hey, look at that! The stupid Maximum-method is gone! But how?

Since you all are attentional readers, of course you have noticed that Math.Max has moved to the arguments of Choose. It’s obviously acting as the Action-parameter, which seems clear, but it’s surrounded by that nebulous x and that thing that looks like an arrow…

Well, that is just the syntax of anonymous functions. Hence they can have a return type as well as parameters but lack a name, they need to have another way of defining both.
It is done by writing a list of parameters (or just a single one, like we did) followed by the arrow.
Everything before the arrow, separated by commas, is handled as the parameter list while everything after the arrow defines the method body.
If possible, the compiler will choose the fitting parameter types on it’s own, so usually you don’t have to worry about that.

In many cases, the anonymous method’s body will consist of one single expression that defines both the operation and the return value.
There is no need of adding a semicolon at the end of the expression.
Actually, it would not even compile.
In some cases however, you might want to do some more steps within the body.
In those cases, you can surround the whole definition of the anonymous method by curled brackets like in the next example:

List<int> values = new List<int>(){1, 2, 3};
List<int> doubles = new List<int>();

values.ForEach(x =>
{
    Console.WriteLine($"{x} will be doubled.");
    doubles.Add(x);
});

So that’s basically it. Not much more to say. πŸ™‚

Now that you now how it works, you should definitely have a look at the Collection-methods provided by the System.Linq namespace.
The List.ForEach method that I used in the example above is one of them and as you can see, it provides a very nice and short way of doing simple operations on a list.
There a many more Methods like that, like Select(), Where() an so on.
By combining them, you can create incredibly powerful one-line-statements that can replace complicated, nested loops.

Events

To be added soon… sorry. 😦

Β 

Advertisements