(C# Language) Delegates, Lambda Expressions, Action and Func Delegates

In this tutorial we learn about delegates that are a data-type for a function signature. In this context a function signature includes the return type and the arguments of a function, but doesn't include the name. We also learn about lambda expressions and built-in delegates called Action and Func delegates.
(Rev. 18-Jun-2024)

Categories | About |     |  

Parveen,

Table of Contents (top down ↓)

The Concept of a Delegate

A delegate is a datatype that references a function or method. Consider the code that defines a delegate called GetBig. We have added a delegate keyword before the function signature. The delegate GetBig can hold any function that matches this signature, can hold any function that returns an int and accepts two int type of arguments.

Next we have added a function called Greater that returns the greater of the two arguments passed to it.

After that we have added another function called Higher that too has the same matching signature. It might include a different logic to determine the return value.

So we have two different functions of the same type - each accepts two ints and returns an int. This means that the delegate GetBig can point to either of them.


class Program
{
  // define a delegate 
  delegate int GetBig (int i, int j);

  static int Greater(int x, int y)
  {
    return x > y ? x : y;
  }

  static int Higher(int x, int y)
  {
    // any complex pre-calculation 
    if(x > 100)
    {
      return y;
    }

    return x > y ? x : y;
  }

  static void Main()
  {
    GetBig fx;

    Console.WriteLine("Type 1 for Greater else Higher: ");

    if("1".Equals(Console.ReadLine()))
    {
      fx = Greater;
    }
    else
    {
      fx = Higher;
    }

    int ret = fx(4, 5);

  }
}

Inside Main we have declared a variable fx of delegate type GetBig.

Next we ask the user to type 1 if the function "Greater" is to be called.

If the user enters 1, then the fx contains Greater. The function Greater is compatible to fx.

Otherwise fx points to the other function called "Higher".

Finally, the delegate type fx can be called by passing two arguments and storing the return type in an int type of variable.

Thus, delegates are data-types that hold functions of a compatible signature.

Video Explanation (see it happen!)

Please watch the following youtube video:

Lambda Expressions

Lambda expressions are used to create anonymous functions.

Consider this lambda expression that specifies two input parameters. Notice that they are specified on the left side of the lambda operator (=>), and the compiler can infer their data-types from context of use. The body contains just one expression. Such lambda expressions are called statement lambdas. No return statement is required because the compiler can infer the return type.


// statement lambda 

(m, n) => (m > n ? m : n);

Let us write the previous program using lambda expressions.

First of all have defined the GetBig delegate.

Then we have the main function where we have declared a variable fx of the data-type GetBig. Next we ask the user to enter 1 for his choice. If he enters 1, then fx is set equal to an expression lambda that takes two arguments and returns the greater of the two.

In the else part fx is set equal to a statement lambda that takes two arguments and has an expression body of multiple statements. A statement lambda has to explicitly use the return statement for returning its result. The code is much simpler now.

Lastly, we have made a call to the delegate fx by passing two arguments.


class Program
{
  // define a delegate 
  delegate int GetBig (int i, int j);
 
  static void Main()
  {
    // declared a variable to hold the delegate 
    GetBig fx;

    Console.WriteLine("Type 1 for Greater else Higher: ");

    if("1".Equals(Console.ReadLine()))
    {
      fx = (m, n) => (m > n ? m : n);
    }
    else
    {
      fx = (m, n) => {

        // any complex pre-calculation 
        if (m > 100)
        {
          return n;
        }

        return m > n ? m : n;

      };
    }

    int ret = fx(4, 5);

    Console.WriteLine($"ret = {ret}");

  }
}

Action and Func Delegates

The System namespace provides built-in delegates that serve our most common needs - so we do not have to specifically define a delegate in our programs - like we defined delegate int GetBig (int i, int j);

The Action delegate has more than 15 variants. All variants of Action have a return type of void.

This is an example of the simplest Action - it takes no arguments - and returns a void - public delegate void Action();

An example of an Action that takes one parameter is public delegate void Action<in T>(T obj); This is generic type - so it can have any type of argument.

Consider this program where printSquare is an instance of an Action that accepts an int type of argument. We didn't have to define any delegate because this delegate is already defined for us in the System namespace.

A lambda statement has been used for writing the function. Lastly, the function has been called.


// action delegates return void 
// this is an example where an action 
// delegate takes one arg, but overloads 
// are available that can 
// take more than 15 

class Program
{
  static void Main()
  {
    Action<int> printSquare = (i) => { Console.WriteLine(i * i); };

    // call the delegate 
    printSquare(3);

  }
}

Next let us come to the Func delegates.

The System namespace provides various flavors of Func delegates also. The only difference between a Func and Action delegate is that Func delegates return a value, whereas Action delegates do not return any value - the return type is void.

This is an example of a Func delegate that returns a value, but accepts no arguments - public delegate TResult Func<out TResult>();

And this is an example that takes one argument public delegate TResult Func<in T,out TResult>(T arg);

In the same program we have a getSquare that returns the square of a number.

In the next statement we use this function to obtain the square of a number. Finally, the result is printed.


// func delegates return data 
// this is an example where a func 
// delegate takes one arg, but overloads 
// are available that can 
// take more than 15 

static void Main()
{
  Action<int> printSquare = (i) => { Console.WriteLine(i * i); };

  // call the delegate 
  printSquare(3);

  Func<int, int> getSquare = (i) => { return i * i; };

  int ret = getSquare(3);

  Console.WriteLine(ret);

}

You can obtain the source code from the attached downloads. Thanks!


This Blog Post/Article "(C# Language) Delegates, Lambda Expressions, Action and Func Delegates" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.