Click here to Skip to main content
Click here to Skip to main content

A Beginner's Tutorial on Basics of Delegates, Anonymous Functions and Lambda Expressions in C#

, 7 Dec 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
This article talks about the delegates and how to write delegate handlers using functions, anonymous functions and Lambda expressions.

Introduction

This article talks about the delegates and how to write delegate handlers using functions, anonymous functions and Lambda expressions. This article is written from a beginner's perspective and contain very introductory material on the related topics. 

Background

There are many a times when we need one part/object of our application to do something when some other part/object has been changed. One way to achieve this is by having an object poll continuously to another object and act whenever the other objects state is changed. Now this approach is not elegant it involves too much of CPU usage. Also, there will be some time interval between 2 consecutive poll request and if the other objects state was changed during this period then we will get to know it later i.e. delayed information.

Now the way this problem is solved is called the Observer Pattern. Here the object who wants to listen to the state change can subscribe to the object whose state will be changed. If we need to put this mechanism in a C# application then we need to take care of both the aspects of this pattern. One is sending the state change from an object to the listener(all the listeners) and receiving the state change of the object.

Delegates is the C# way of implementing the first part i.e. keeping a track of all the listeners and sending them the notification when any state is changed. Anonymous methods and Lambda expressions come on the other side i.e. handling the response coming from a delegate

Using the code

Let us try to implement a simple application with a class called BatteryLevel to understand these topics in action. This class will keep track of the current battery level of a device. Other classes can subscribe to a delegate provided by this class to listen to battery level change. Let us look at this class without any delegates introduced.

class BatteryLevel
{
    int currentBatteryLevel;

    public int CurrentBatteryLevel
    {
        get
        {
            return currentBatteryLevel;
        }
        set
        {
            // Some process will call this setter to set the battery level
            currentBatteryLevel = value;
        }
    }
}

To implement delegates and its handlers there are mainly 4 steps involved:

  1. Declaring the delegate
  2. Creating the delegate
  3. Hooking the delegate with handler functions
  4. Invoking the delegates

Now before adding a delegate to this class lets get some basics clear. Delegates are types which we can define. The definition will declare the delegate and specify the method signature that this delegate can hold and call. Typical delegate declaration will look like:

[Access Modifier] delegate [Return type] [DELEGATE_NAME]([Function arguments]);

So to declare a delegate for our class, lets say we want the handler functions to return void and accept an integer value indicating the new battery level. The declaration will look like:

// This is the delgate type, this will specify the name of the delegate
// and the function signature of the function that this delgaete can call
public delegate void BatteryLevelBroadcaster(int batteryLevel);

Delegates can hold reference to a number of methods and when a delegate is invoked, it will call all the methods one by one sequentially. C++ programmers can think of delegates as a list of type safe function pointers.

Now we have seen the first part of our 4 step process. Now if we move on to the second part i.e Creating the delegate, we simply need create a type of the delegate and assign some handler functions to it. if no handlers are associated with the delegate object it will be null. So lets create a delegate type first.

// This is the instance of out delegate type which will hold
// list of functions to call too
public BatteryLevelBroadcaster batteryLevelBroadcaster;

Now we have seen the second part of our 4 step process. Now if we move on to the fourth part i.e. invoking the delegate. The the typical way to call all the functions associated with this delegate would be to call the delegate like a function.

//lets check whther someone has registered for listening or not
if (batteryLevelBroadcaster != null)
{
    // Lets call all the functions with new battery level value
    batteryLevelBroadcaster(currentBatteryLevel);
}

So now the BatteryLevel class definition with the delegate will look like:

class BatteryLevel
{
    int currentBatteryLevel;

    // This is the delgate type, this will specify the name of the delegate
    // and the function signature of the function that this delgaet can call
    public delegate void BatteryLevelBroadcaster(int batteryLevel);

    // This is the instance of out delegate type which will hold
    // list of functions to call too
    public BatteryLevelBroadcaster batteryLevelBroadcaster;

    public int CurrentBatteryLevel
    {
        get
        {
            return currentBatteryLevel;
        }
        set
        {
            // Some process will call this setter to set the battery level
            currentBatteryLevel = value;

            //lets check whther someone has registered for listening or not
            if (batteryLevelBroadcaster != null)
            {
                // Lets call all the functions with new battery level value
                batteryLevelBroadcaster(currentBatteryLevel);
            }
        }
    }
}

Now we have seen the invoker objects part of implementing delegates i.e. declaring, creating and invoking a delegate. Now let us see how the handing part can be written i.e. hooking the functions to the delegates and having the handler function's body.

To hook a function to the delegate we simply need to create the delegate type with the function name passed in the constructor. We can then assign this delegate type to the delegate exposed by the class.

static void Main(string[] args)
{
    // lets say we have a module that notifies the user with low battery
    // here we are emulating it.
    BatteryLevel batteryLevel = new BatteryLevel();

    // Lets subscribe to the battery level delegate to know the current level
    batteryLevel.batteryLevelBroadcaster += new BatteryLevel.BatteryLevelBroadcaster(BatteryLevelIndicator);
    
    // Now let us simualte the hardware function that will set the battery level
    batteryLevel.CurrentBatteryLevel = 35;
    batteryLevel.CurrentBatteryLevel = 30;
    batteryLevel.CurrentBatteryLevel = 25;
    batteryLevel.CurrentBatteryLevel = 20;
    batteryLevel.CurrentBatteryLevel = 15;
    // Once the above code is run, for each updation we should get the
    // message written in our delgegate function
}

// This is the function that will let the user know the current battery level
public static void BatteryLevelIndicator(int newValue)
{
    Console.WriteLine("New battery level is: {0}", newValue);
}

The above code simply creates a function and then attach that function to the delegate exposed by the class as the handler function. the function signature matches the delegate signature otherwise the code would not compile.

We are also setting the value of battery level to test the application. Whenever we call set our handler function will be invoked and it will print the message on the console.

Note: We have used += to assign the delegate because we need our function to add to the list of functions already added to this delegate. If we simply use = sign, it will wipe off the list of existing functions and have this function as the only function associated with the delegate.

So we have seen the 3rd part of our 4 step process i.e. hooking up the handler functions with the delegates. When we run the application we can see the handler function getting called via the delegate.

Anonymous functions

Anonymous functions are the functions without any function name. C# gives us the possibility of creating functions without name. But how will this function be called if it has no name. In fact there is no way to call an anonymous function via code because the name of the function is the handle to call this function.

But we have just seen that the delegates keep handles/references of all the handler functions with them. delegates don't need the name of the function to invoke them. so the anonymous functions are useful with the delegates only.

What anonymous function does is that instead of having separate code to hook the handler to delegate and body of the handler function we can simply have the body written in place where we are hooking the function to the delegate. So if I need to achieve the above functionality using anonymous function, I simply need to write it as:

static void Main(string[] args)
{
    // lets say we have a module that notifies the user with low battery
    // here we are emulating it.
    BatteryLevel batteryLevel = new BatteryLevel();

    // Here we have another module that is listening to the
    // battery level change but this time lets implement this using the
    // anonymous function
    batteryLevel.batteryLevelBroadcaster += delegate(int newValue) { Console.WriteLine("(ANOMYMOUS)New battery level is: {0}", newValue);};

    // Now let us simualte the hardware function that will set the battery level
    batteryLevel.CurrentBatteryLevel = 35;
    batteryLevel.CurrentBatteryLevel = 30;
    batteryLevel.CurrentBatteryLevel = 25;
    batteryLevel.CurrentBatteryLevel = 20;
    batteryLevel.CurrentBatteryLevel = 15;
    // Once the above code is run, for each updation we should get the
    // message written in our delgegate function
}

And it will produce the same result as the earlier version but it doesn't need the programmer to write a separate function. this is very useful when the handler function has little code and using this approach gives a cleaner code compared to the earlier version.

Lambda Expressions

Newer version of C# has Lambda expression which has made the anonymous functions more or less obsolete. For the scope of this article lets understand lambda expression is one more way of creating local anonymous functions that can be used with delegates.

Note: Lambda expression has other merits over anonymous function like the can directly be assigned to expression trees but we will keep things simple in this article and will not talk about these.

A Lambda expression can be thought of as an alternative to the anonymous methods with the following syntax:

[METHOD PARAMETERS] => [METHOD BODY]

So if were to write the similar code using lambda expression we can do that in the following manner:

static void Main(string[] args)
{
    // lets say we have a module that notifies the user with low battery
    // here we are emulating it.
    BatteryLevel batteryLevel = new BatteryLevel();

    // And finally there is the third approach where we can use Lambda expression
    // to subsribe to the delegates
    batteryLevel.batteryLevelBroadcaster += (newValue) => Console.WriteLine("(LAMBDA)New battery level is: {0}", newValue);
    // ABOVE LINE WILL WORK ONLY IN VS2008 and above

    // Now let us simualte the hardware function that will set the battery level
    batteryLevel.CurrentBatteryLevel = 35;
    batteryLevel.CurrentBatteryLevel = 30;
    batteryLevel.CurrentBatteryLevel = 25;
    batteryLevel.CurrentBatteryLevel = 20;
    batteryLevel.CurrentBatteryLevel = 15;
    // Once the above code is run, for each updation we should get the
    // message written in our delgegate function
}

Note: in this above code instead of having the implicit type parameter in lambda expression, we could have specified the type in the expression too.

batteryLevel.batteryLevelBroadcaster += (int newValue) => Console.WriteLine("(LAMBDA)New battery level is: {0}", newValue);

The result of the above code is same as the earlier versions but the code is much cleaner compared to the earlier versions(one with the handler functions and anonymous methods).

Point of Interest

In this small article we tried to look into what delegates are and how we can implement custom delegates and their handlers. Delegates have been around from quite some time and this article seems quite late or out of time. I have written this article because someone in the Codeproject Q&A section seems quite confused about the topic. This article has been written from a beginner's perspective. I hope this has been somewhat informative.

History

  • 06 December 2012: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Rahul Rajat Singh
Architect
India India
I Started my Programming career with C++. Later got a chance to develop Windows Form applications using C#. Currently using C#, ASP.NET & ASP.NET MVC to create Information Systems, e-commerce/e-governance Portals and Data driven websites.

My interests involves Programming, Website development and Learning/Teaching subjects related to Computer Science/Information Systems. IMO, C# is the best programming language and I love working with C# and other Microsoft Technologies.
  • Microsoft Certified Technology Specialist (MCTS): Web Applications Development with Microsoft .NET Framework 4
  • Microsoft Certified Technology Specialist (MCTS): Accessing Data with Microsoft .NET Framework 4
  • Microsoft Certified Technology Specialist (MCTS): Windows Communication Foundation Development with Microsoft .NET Framework 4
 
If you like my articles, please visit my website for more: www.rahulrajatsingh.com[^]
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralMy vote of 5 PinmemberGuyThiebaut7-Dec-12 4:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 8 Dec 2012
Article Copyright 2012 by Rahul Rajat Singh
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid