in C#

Using delegates in C#

Today we are going to learn what delegates are, how can we create our custom delegates, defining and raising events, lambda delegates and asynchronous delegates. We will also learn when we can use the above mentioned concepts and how they can improve our productivity and code debugging.

You can find the whole source code for this blog post here

What are delegates?

A delegate is a type that represents references to methods with a particular parameter list and return type. Delegates are used to pass methods as arguments to other methods. You create a custom method, and a class such as a windows control can call your method when a certain event occurs.

More on the official MSDN documentation

What are events?

Events enable a class or object to notify other classes or objects when something of interest occurs. The class that sends (or raises) the event is called the publisher and the classes that receive (or handle) the event are called subscribers.

More on the official MSDN documentation

Understanding ExampleBase.cs

In order to showcase delegates we need, obviously, a delegate method, hence the delegate void ExecuteOperation(double x, double y). This particular example uses basic mathematical operations, hence the Add, Substract, Multiply, Divide methods. To provide a feedback and get the results of those mathematical operations we need to print something to the screen, hence the void DisplayResult(Operation operationType, double value). In order to abstractly print the result to the screen while maintaing the operation it processed, we need an extra argument, hence the Operation enum. In order to encapsulate the functionality, we need a way to put everything in motion while also maintaing privacy, hence the public abstract void Run() method wich will be implemented in every child class.

Simple delegates

To keep it simple, delegates are methods used as variables, similar to pointers to functions in C. In this case, we have one delegate variable per mathematical operation, so in order to compute each result we have to call each delegate separately.

var addOperation = new ExecuteOperation(AddOperation);
var substractOperation = new ExecuteOperation(SubstractOperation);
var multiplyOperation = new ExecuteOperation(MultiplyOperation);
var divideOperation = new ExecuteOperation(DivideOperation);

addOperation(2, 3);
substractOperation(5, 3);
multiplyOperation(10, 2);
divideOperation(20, 4);

01

Multiple delegates

We can have multiple delegates assigned to the same variable, in which case all methods are called. To add a delegate to a variabile we use the += operator and for removing a delegate, we use the -= delegate. Imagine a scenario where whenever a person enters the room, a jingle bell will be heard. Now imagine that you want to extend the functionality to also take a picture of the person entering the room. In order to do this you will have to integrate your logging system without any knowledge about how the original code was written, you have to dig the code for the place where the event is called and so on. But if you implemented with delegates, it is easy to expose the delegate method in an interface and all you have to do is implement that method and simply add your picture logging delegate action to the delegate object.

ExecuteOperation mathOperations = null;

mathOperations += new ExecuteOperation(AddOperation);
mathOperations += new ExecuteOperation(SubstractOperation);
mathOperations += new ExecuteOperation(MultiplyOperation);
mathOperations += new ExecuteOperation(DivideOperation);

mathOperations(40, 5);

01

Anonymous methods

In versions of C# before 2.0, the only way to declare a delegate was to use named methods. C# 2.0 introduced anonymous methods and in C# 3.0 and later, lambda expressions supersede anonymous methods as the preferred way to write inline code. By using anonymous methods, you reduce the coding overhead in instantiating delegates because you do not have to create a separate method.

More on the official MSDN documentation

If you don’t want to create a method on its own, you can create an anonymous one instead and place it inside the method you want to use it. The functionality is the same as a named method, only that the scope is limited to the context in which you write it.

ExecuteOperation addOperation = delegate (double x, double y)
{
    DisplayResult(Operation.Add, x + y);
};

ExecuteOperation substractOperation = delegate (double x, double y)
{
    DisplayResult(Operation.Substract, x - y);
};

ExecuteOperation multiplyOperation = delegate (double x, double y)
{
    DisplayResult(Operation.Multiply, x * y);
};

ExecuteOperation divideOperation = delegate (double x, double y)
{
    DisplayResult(Operation.Divide, x / y);
};

addOperation(2, 3);
substractOperation(5, 3);
multiplyOperation(10, 2);
divideOperation(20, 4);

01

Lambda delegates

A lambda expression is an anonymous function that you can use to create delegates or expression tree types. By using lambda expressions, you can write local functions that can be passed as arguments or returned as the value of function calls. Lambda expressions are particularly helpful for writing LINQ query expressions.

More on the official MSDN documentation

If you don’t want to create a method on its own, and you want to rely on the lambda calculus concept you can create a lambda method, which basically is a plain anonymous method. The functionality is the same as a named or anonymous method, but the power of lambda methods comes with LINQ query expressions (more on this soon 😎).

ExecuteOperation addOperation = (x, y) => DisplayResult(Operation.Add, x + y);

ExecuteOperation substractOperation = (x, y) => DisplayResult(Operation.Substract, x - y);

ExecuteOperation multiplyOperation = (x, y) =>
{
    DisplayResult(Operation.Multiply, x * y);
};

ExecuteOperation divideOperation = (x, y) =>
{
    DisplayResult(Operation.Divide, x / y);
};

addOperation(2, 3);
substractOperation(5, 3);
multiplyOperation(10, 2);
divideOperation(20, 4);

Asynchronous delegates

If your brain didn’t melt by now you’re good, but I am sure that with this chapter it will. All delegates have the ability to run on threads, which is a very cool feature introduced by the Asynchronous Programming Model (APM) (you can check more about this topic here). In this example, we run each mathematical operation on a separate thread and after it finishes we run a callback in which we terminate the thread. In this example, after a thread finishes its work, it prints a message to the screen. We then call the EndInvoke method to terminate the thread which does not necessarily end when the main thread ends nor does the operations or callbacks executed in the written order (as you can see in the output). So in order for us to see the messages from the callback methods, we need to wait for them to finish either by using async/await or by synchronization with the main thread (putting the main thread on sleep because thread synchronization isn’t the purpose of this post) (more on multithreading here).

private void ExecuteOperationCallback(IAsyncResult asyncResult)
{
    var result = asyncResult as AsyncResult;
    var caller = result.AsyncDelegate as ExecuteOperation;
    var operation = (Operation)result.AsyncState;

    Console.WriteLine("--> The {0} operation finished", operation);

    caller.EndInvoke(asyncResult);
}

public override void Run()
{
    Console.WriteLine("=== Asynchronous Delegates Example ===");

    var addOperation = new ExecuteOperation(AddOperation);
    var substractOperation = new ExecuteOperation(SubstractOperation);
    var multiplyOperation = new ExecuteOperation(MultiplyOperation);
    var divideOperation = new ExecuteOperation(DivideOperation);

    var operationCallback = new AsyncCallback(ExecuteOperationCallback);

    addOperation.BeginInvoke(2, 3, operationCallback, Operation.Add);
    substractOperation.BeginInvoke(5, 3, operationCallback, Operation.Substract);
    multiplyOperation.BeginInvoke(10, 2, operationCallback, Operation.Multiply);
    divideOperation.BeginInvoke(20, 4, operationCallback, Operation.Divide);

    /*
     * We need this because the callbacks are made on background threads
     * which don't necessarily finish when the main thread exits
     */
    Thread.Sleep(1000);
    Console.WriteLine();
}

01

In conclusion

As you may have observed, a bit of C# coding knowledge is required. For a better understanding of how powerful the C# language really is, you can check out this repository full with basic C# projects. If you want to go deeply into advanced C# topics, you can check out this repository.

So there you have it, a well documented post about basic delegates, lambda delegates and asynchronous delegates. Stay tuned on this blog (and star the microsoft-dx organization) to emerge in the beautiful world of “there’s an app for that”.

Post image source: guim.co.uk

Related Posts

Leave a Reply