Delegates, Anonymous Method, And Lambda Expression In C#

Introduction

In C# and .NET, many developers struggle to build a conceptual understanding of delegates. In my team, during presentations, many people ask to discuss delegate. Once my senior at the Pune office said that when C# comes to delegates, it looks like a new programming language. Delegates can be compared to function pointers in C++ but they have many other concepts based on it. In this article, I will talk about what a delegate is and how you can extend its concept in the world of C# and . NET.

Background

Delegates are heavily used in Events, LINQ, .NET v 3.5 onwards, extension methods, and ASP.NET MVC, etc. I will give you a very comprehensive explanation about delegates that is based on etymologies, like the Anonymous Method and Lambda Expression. Actually, there are a lot of other things that can be discussed pertaining to delegate but that will be a separate discussion and I shall discuss that part possibly another time. In this article, you will get knowledge about three major concepts very easily.

  • Delegates have been a part of C# since version 1.0 was released. A delegate holds a reference to a method. Delegates can be defined in two types: Singlecast and Multicast (Both types are discussed below in code).
  • The Anonymous Method is an inline code that can be used wherever a delegate type is expected. Microsoft introduced Anonymous Methods in C# 2.0 somewhere around 2003.
  • A lambda expression is an anonymous method that you can use to create delegates or expression tree types. Microsoft introduced Lambda Expression in C# 3.0 sometime in 2007. Most of the time, anonymous methods are confused with lambda expressions by many developers. I have discussed this in detail here. You know that there is one case in which an anonymous method provides functionality not found in lambda expressions, as anonymous methods enable you to omit the parameter(s).

Using Code

using System;

namespace DAL
{
    public delegate void MyDelegate();

    class Program
    {
        public void CallerOne()
        {
            Console.WriteLine("This is Caller One function...");
        }

        static void Main()
        {
            // Create an instance of the class
            Program p = new Program();

            // Create an instance of the delegate and pass the method as a parameter
            MyDelegate myDelegate = new MyDelegate(p.CallerOne);
            myDelegate();
        }
    }
}

You can see that I have a delegate named MyDelegate and as you know, it can hold a reference to a Method. So, MyDelegate holds a reference to a Method CallerOne(). Know that you can also execute the method with myDelegate.Invoke(); instead of calling delegate object myDelegate();. This definition will remain similar in the following code too. One more point to note - Here, look at the return type and parameters. Delegate and Method signatures must be the same to implement it. Let's go next to copy and paste the below code into your console application's Program.cs.

using System;

namespace DAL
{
    public delegate void MyDelegate();

    class Program
    {
        public void CallerOne()
        {
            Console.WriteLine("This is Caller One function...");
        }

        public void CallerTwo()
        {
            Console.WriteLine("This is Caller Two function...");
        }

        static void Main()
        {
            // Create an instance of the class
            Program p = new Program();

            // Create an instance of the delegate and pass the method as a parameter
            MyDelegate myDelegate = new MyDelegate(p.CallerOne);

            // Create another instance of the delegate and pass a different method as a parameter
            MyDelegate myDelegate1 = new MyDelegate(p.CallerTwo);

            // Invoke the delegate instances
            myDelegate();
            myDelegate1();
        }
    }
}

In the above step, there is another object of Delegate named myDelegate1 but it does a similar job as in the first code. So, we have implemented two objects of the same delegate. Let's next copy and paste the below code in your console application's Program.cs.

using System;

namespace DAL
{
    public delegate void MyDelegate();

    class Program
    {
        public void CallerOne()
        {
            Console.WriteLine("This is Caller One function...");
        }

        public void CallerTwo()
        {
            Console.WriteLine("This is Caller Two function...");
        }

        public static void CallerThree()
        {
            Console.WriteLine("This is Caller Three function...");
        }

        static void Main()
        {
            // Create an instance of the class
            Program p = new Program();

            // Create an instance of the delegate and pass the method as a parameter
            MyDelegate myDelegate = new MyDelegate(p.CallerOne);

            // Create another instance of the delegate and pass a different method as a parameter
            MyDelegate myDelegate1 = new MyDelegate(p.CallerTwo);

            // Create another instance of the delegate and pass a static method as a parameter
            MyDelegate myDelegate2 = new MyDelegate(CallerThree);

            // Invoke the delegate instances
            myDelegate();
            myDelegate1();
            myDelegate2();
        }
    }
}

Think about the above code! Why should I not allocate the memory to the method at compile time, instead of calling member function/ method using class object? So, I marked CallerThree as a static method.

using System;

namespace DAL
{
    public delegate void MyDelegate();

    class Program
    {
        public void CallerOne()
        {
            Console.WriteLine("This is Caller One function...");
        }

        public void CallerTwo()
        {
            Console.WriteLine("This is Caller Two function...");
        }

        public static void CallerThree()
        {
            Console.WriteLine("This is Caller Three function...");
        }

        static void Main()
        {
            // Create an instance of the class
            Program p = new Program();

            // Create an instance of the delegate and pass the method as a parameter. This is Single Cast
            MyDelegate myDelegate = new MyDelegate(p.CallerOne);

            // Create another instance of the delegate and pass a different method as a parameter. This is Single Cast
            MyDelegate myDelegate1 = new MyDelegate(p.CallerTwo);

            // Create another instance of the delegate and pass a static method as a parameter. This is Single Cast
            MyDelegate myDelegate2 = new MyDelegate(CallerThree);

            myDelegate();
            myDelegate1();
            myDelegate2();

            // This concept is called Multi Cast
            MyDelegate MyDelegate_MultiCast_Idea = myDelegate + myDelegate1 + myDelegate2;
            MyDelegate_MultiCast_Idea();
        }
    }
}

Look at the code above. MyDelegate_MultiCast_Idea object is the sum of all previous objects. This is called Multi-Casting in delegates. At this point, you shall be clear that the first, second, and third codes from the starting article will have 1, 2, and 3 Singlecast delegates respectively, for each of the individual methods.

Now, pay attention! The requirement to implement a delegate, we have to have a method defined but that is complicated. If I take this idea to 3 tier architecture that means changing Presentation Layer has to be reflected in Business Layer and Data Layer. In short, changes will have an impact! So, think about something where we can write the method itself and pass its reference. Microsoft did this job with C# 2.0 by introducing Anonymous Methods.

using System;

namespace DAL
{
    public delegate void MyDelegate();

    class Program
    {
        static void Main()
        {
            // I have passed the full method body within the delegate instance
            // This method doesn't have a name
            MyDelegate myDelegate = new MyDelegate(delegate()
            {
                Console.WriteLine("This is Caller One function...");
            });

            MyDelegate myDelegate1 = new MyDelegate(delegate()
            {
                Console.WriteLine("This is Caller Two function...");
            });

            MyDelegate myDelegate2 = new MyDelegate(delegate()
            {
                Console.WriteLine("This is Caller Three function...");
            });

            myDelegate();
            myDelegate1();
            myDelegate2();
        }
    }
}

Absolutely clean and clear code! A delegate holds reference to a method but here delegate holds the method itself.. so what is the name of the method? No name!! That is why it is called the Anonymous Method!

As mentioned earlier in the article, a delegate's signature must match with the method it takes in reference and vice versa. There is one case in which an anonymous method provides functionality not found in Lambda Expression. Anonymous methods enable you to omit the parameter(s). This feature is very important in both ways. It can be used to make the system more flexible by using the Anonymous Method and or more strongly typed using Lambda Expression. This comparison is given after the code.

using System;

namespace DAL
{
    public delegate void MyDelegate();

    class Program
    {
        static void Main()
        {
            // Here '=>' is used to implement Lambda Expression.
            // This can be further discussed, but I will cover it in a future article.
            MyDelegate myDelegate = new MyDelegate(() =>
            {
                Console.WriteLine("This is Caller One function...");
            });

            myDelegate();
        }
    }
}

Let's have a comparison on Anonymous Method vs. Lambda Expression. This example will give at least two ideas. The first one is working with parameterized delegates and the second one is the comparison between Anonymous Method and Lambda Expression.

using System;

namespace DAL
{
    public delegate void MyDelegate(int num);

    class Program
    {
        static void Main()
        {
            MyDelegate myDelegate = new MyDelegate(delegate (int num)
            {
                Console.WriteLine("This is Caller One function...");
            });

            myDelegate(211);
        }
    }
}

You can take note that you have passed parameter num value 211 but the Anonymous Method can omit it and the program is executed. Now, with the same delegate, look at the below code which uses Lambda Expression. It will throw a compile time error because you will have to pass a parameter in the method body to compile and execute. How to pass the parameter in this? I leave this very simple job up to you and you have to post your answer in the comments.

using System;

namespace DAL
{
    public delegate void MyDelegate(int num);

    class Program
    {
        static void Main()
        {
            MyDelegate myDelegate = new MyDelegate(delegate(int num)
            {
                Console.WriteLine("This is Caller One function...");
            });

            myDelegate(211);
        }
    }
}

Hint - Pass your parameter (here) => Method Body

Conclusion

I tried my best to deliver this great idea to you. Hope you enjoyed it! Further, I will talk about Action, Predicate, and Func, probably by next week sometime.

Thanks! Happy Coding..!


Similar Articles