Delegates, Anonymous Functions and Lambda Expressions in C#

What's in this article?

  • Introduction
  • Delegate
  • Delegate Declaration
  • Use Delegate
  • Type Of Delegate
    • Singlecast Delegate
    • Multicast Delegate
  • Action<T> and Func<T> Delegates
  • Delegate Evolution
  • Anonymous Method
  • Lambda Expressions
    • Variable scope with lambda expression
  • End of the Article

1. Introduction

This article provides the basics of delegates, Lambda expressions and delegate with Lambda Expressions.

2. What is Delegate?

Delegates are a class that holds function references for .Net (more specifically, the Common Language Infrastructure (CLI)). If you compare them with C++ function pointers, C++ function pointers do nothing more than hold the memory location of a function. And the main difference between C++ function pointers and C# delegates is, a function pointer is not type safe whereas C# delegates are a type safe class that defines a return type and a type of parameter that can be passed for methods. C# delegates are the same as passing methods to another method. A C# delegate is type safe because whenever you need to declare any delegate in your program you need to specify the return type and parameter numbers, type and sequence of methods that's going to be passed for delegates.

3. How to declare a delegate?

For declaring a delegate in a C# program you can use the following syntax:

<Access Modifier> <delegate> <Return Type> <Delegate Name>(<Parameter's>)

Where:

  • <Access Modifier> can be public, private, protected and so on.
  • <Delegate> is a pre-defined keyword in C#.
  • <Return Type> can be any type depending on your referenced function.
  • <Delegate Name> can be any name.
  • <Parameter> will also depend on the referenced function.

How to implement the preceding syntax in a C# program?

public delegate int Del(int Value);

In this case, you just declare a delegate Del and indicate that each instance of the Del delegate can hold a reference to a method that takes a single int parameter and returns an int.

How to initialize a delegate?

As I said above, a Delegate is a class, so you can initialize a delegate the same as you initialize a class.

<Delegate Name> <Delegate Object> = <new> Delegate(<Parameter's>)

How to implement the preceding syntax?

Del obj = new Del(fun);

Where:

"fun" is the name of the function to be passed as a reference to the delegate Del.

How to invoke a function via a delegate?

obj();

Just a minute

Actually here is what happened internally. Whenever you declare a delegate in your program, the C# compiler generates a sealed class internally that's derived from a predefined class System.MulticastDelegate and the Multicast delegate is derived from the System.Delegate class, and both the MulticastDelegate and Delegate Classes are abstract types internally. That's why you are able to call methods as Invoke() and others that are directly or indirectly belonging to the System.MulticastDelegate class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Anonymous
{

    class Program
    {
        public delegate int fun(int Value);

        public static int del(int Value)
        {
            return Value += 5;
        }

        static void Main(string[] args)
        {
            Program okkk = new Program();
            fun ok = new fun(del);
            Console.WriteLine(ok(50));

            Type type = ok.GetType();
            Console.WriteLine("\n Base Class for ok Delegate type:\t"+type.BaseType);
            Console.WriteLine("\n Is Class : \t"+type.IsClass);
            Console.WriteLine("\n Is Sealed Class : \t"+type.IsSealed);
            Console.ReadLine();
           
        }
    }
}

4. Use of a delegate in a C# Program

By default a delegate takes a single instance function or static function as a parameter.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lambadas_Expression
{
    public delegate void Del();

    class Class1
    {

        public void fun()
        {
            Console.WriteLine("Class1.fun");
        }

        static void Main(string[] args)
        {

            Class1 c1 = new Class1();
            Del obj = new Del(c1.fun);
            obj.Invoke();
        }
    }
}

In the program above I just created a Delegate with the name Del and there is no parameter and return type as void. So whenever you instansiate a Delegate in such a situation you can pass a function reference that matches the same return type and the same type, number and sequence of parameters. Since I pass a reference of the fun that is a type of instance function. And for invoking the function fun you can use the Invoke function or you can directly use a delegate object as obj() to invoke the function for which you have passed a reference.

5. Type of Delegate

Delegates are one of two types:

  1. Singlecast Delegate

  2. Multicast Delegate

Singlecast Delegate

A Singlecast delegate is derived from the System.Delegate class. It can contain a reference for one method at a time. In the above example the delegate uses a Singlecast delegate.

Multicast Delegate

A Multicast delegate is derived from the System.MulticastDelegate class. It can contain references for multiple methods. For understanding multicast delegates you can consider the real-life example: of a Coffee vending machine.

Delegate.jpg

A Coffee vending machine has three containers for Milk, Coffee, and Tea. If you press the Tea button then there will two containers, Milk and Tea that work together. And if you press the button for Coffee then the Milk and Coffee containers will work. So if you want to call multiple functions then you can use a multicast delegate.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Anonymous
{
    class Class1
    {
        public delegate void Vender();

        public static void Milk()
        {
            Console.Write("Milk + ");
        }
 
        public static void Tea()
        {
            Console.Write("Tea = Tea ");
        }
 
        public static void Coffee()
        {
            Console.Write("Coffee = Coffee ");
        }

        static void Main()
        {
            Vender vender = new Vender(Milk);
            Console.WriteLine("\t\tPress 1 For Tea\n\t\t Press 2 For Coffee");
            int Opt = Convert.ToInt32(Console.ReadLine());
            switch (Opt)
            {
                case 1:
                    vender += Tea;
                    break;

                case 2:
                    vender += Coffee;
                    break;

                default:
                    Console.WriteLine("Invalid Option !");
                    Environment.Exit(0);
                    break;
            }

            vender();
            Console.ReadKey();
        }
    }
}

In the above program I just created the three functions, Tea, Coffee and Milk. When one is pressed there will be two functions, called Milk and Tea. If the user chooses both then again the two functions, Milk and Coffee are called. Since Milk is common to both situations I just pass Milk in the Delegate parameter. And the delegate understands "+" and "+=" for adding references of functions and "-" and "-=" for removing function references, so dependng on the option used I just add a function reference with the same Delegate obeject vender. And invoke the delegate vender.

6. Action<T> and Func<T> Delegates

Instead of creating your own delegate for deffrent parameters and return type you can use a predefined Func<T> and Action<T> delegate that is decleaed in the System namespace that is declared of 16 types with different parameters.

7. Delegate Evolution

  • In C# 1.0 you create a delegate instance and initialize with a method reference.

  • C# 2.0 introduced Anonymous methods that can be executed with delegate invocation.

  • Then in C# 3.0, lambda expressions are similar in concept to anonymous methods but more expressive and conches.

These two concepts are collectively known as anonymous functions. An anonymous function is a block of code that can be used as a delegate type as a method parameter.

There are two kinds of anonymous functions:

  1. Anonymous method

  2. Lambdas expression

8. Anonymous Method

In C# 1.0 you can use a delegate and pass a function reference with an initializing delegate with a function name, but C# 2.0 introduced anonymous functions.

Creating an anonymous method as an inline block of code to be passed as a delegate parameter can be done as you see in the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Anonymous
{
    class Class1
    {
        public delegate void Del();

        static void Main(string[] args)
        {
            Del obj = delegate() {
                Console.WriteLine( "Class1.fun");
            };

            obj();
        }
    }
}

Use of an anonymous function can reduce the lines of code. So, here in the program above you can see I just passed a block of code for a delegate parameter inside of creating a function anywhere else and passing the function name. The anonymous function uses the delegate keyword while delegate initialization writes a block of code. You can also pass a parameter for an anonymous function as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Anonymous
{
    class Class1
    {
        public delegate void Del( string Mesg);

        static void Main(string[] args)
        {
            Del obj = delegate(string Mesg) {
                Console.WriteLine(Mesg);
            };

            obj("Class1.fun");
        }
    }
}

Note:

You cannot use goto, break and continue statements inside the anonymous method block if the targat is outside an anonymous block.

9. Lambda Expressions

A Lambda expression is an anonymous function that you can use to create a delegate. Lambda expressions are very useful for writing LINQ queries. For creating a lambda expression you can use the following santax:

(<Paramter>) => { expression or statement; }

If there is any parameter thenb you can specify parameters on the left side of the lambda operator => and put all your expressions or santax on another side. For example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Anonymous
{
    class Class1
    {
        public delegate int Del( int Value);

        static void Main(string[] args)
        {
            Del obj = (Value) => {
                int x=Value*2;
                return x;
            };
            Console.WriteLine(obj(5));
        }
    }
}

Parentheses are optional only if the lambda expression holds more than one parameter the same as if the lambda expression has only a single statement. Curly brakets are also optional; that means you can alternatively write the program above in the following format:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Anonymous
{
    class Class1
    {
        public delegate int Del( int Value);

        static void Main(string[] args)
        {
            Del obj = Value => Value*2;
            Console.WriteLine(obj(5));
        }
    }
}

In the above program you need to notice two things first, there is no return statement in the lambda expression but the delegate Del has a return type as int and the second thing is that I just did not specify any type for the Value variable so in such a situation the lambda expression will be converted into that type implicitly. This conversion is the concept of Type inference as I explained in http://www.c-sharpcorner.com/UploadFile/d6fefe/all-about-generics/.

It is sometimes impossible for the compiler to infer the type of parameters so for that you can specify the type of a parameter, as you can see in the following example:

(int Value1, string Value2) => Value2.Length * Value1;

If there is not a parameter then you need to use blank parenthesis as in: "() => expression;".

10. Variable scope with lambda expression

With lambda expressions you can access a variable declared outside the block of the lambda expression. Here you will wonder how lambda expressions will access the outer variable. Actually the compiler creates an Anonymous class and creates a constructor that takes parameters depending on how many outer variables you used in the lambda expression and creates an anonymous function for declaring the lambda expression as in the following:

class Anon_Class
{
public int Value;
 
public Anon_Class(int Value)
{
    this.Value=Value;
}

public int Anon_Fun(int x)
{
   return x*2;
}

}

But when accessing the outer variable inside the lambda expression, cases can be a problem if you do not use them correctly.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Anonymous
{
    class Class1
    {

        delegate bool Del(int z);
        delegate bool Del2(int z);
        Del2 del2;
        public void Init(int Value)
        {
            int j = 0;

            Del del = (x) => { j = 12; return x == j; };
            del2 = (x) => {return Value == j; };

            Console.WriteLine("j = {0}", j);

            bool Res = del(12);

            Console.WriteLine("j Value= {0} and Res = {1}", j, Res);
        }

        static void Main()
        {
            Class1 class1 = new Class1();
            class1.Init(12);

            //here res will be True Becouse there will be a copy of j variable //with value 12 where as j is local variable

            bool res=class1.del2(12);
            Console.WriteLine("res = "+res);
            Console.ReadKey();
        }
    }

}

The program above will be confusing about the value of the variable j. We cannot recognize which time and where, what value j has. A variable that is captured will not be garbage-collected until the delegate that references it goes out of scope. There are some more rules with lambdas that's defined with an anonymous method as you cannot use a go to, continue and break statement if its target is outside the block.

11. End of the article

You can use lambda expressions in your program where you need to pass a parameter as a delegate. Another use of lambdas is with Expression and Expression<T> classes, for which the compiler creates an expression tree. I intend to write an article for Tree Expression in C# as soon as possible.


Similar Articles