Lambda expressions

A lambda expression is a block of code (an expression or a statement block) that is treated as an object. It can be passed as an argument to methods, and it can also be returned by method calls. Lambda expressions are used extensively for:

Lambda expressions are code that can be represented either as a delegate, or as an expression tree that compiles to a delegate. The specific delegate type of a lambda expression depends on its parameters and return value. Lambda expressions that don't return a value correspond to a specific Action delegate, depending on its number of parameters. Lambda expressions that return a value correspond to a specific Func delegate, depending on its number of parameters. For example, a lambda expression that has two parameters but returns no value corresponds to an Action<T1,T2> delegate. A lambda expression that has one parameter and returns a value corresponds to Func<T,TResult> delegate.

A lambda expression uses =>, the lambda declaration operator, to separate the lambda's parameter list from its executable code. To create a lambda expression, you specify input parameters (if any) on the left side of the lambda operator, and you put the expression or statement block on the other side. For example, the single-line lambda expression x => x * x specifies a parameter that’s named x and returns the value of x squared. You can assign this expression to a delegate type, as the following example shows:

using System;

class Example
{
   public static void Main()
   {
      Func<int, int> square = x => x * x; 
      Console.WriteLine(square(25));
   }
}
// The example displays the following output:
//      625

Or you can pass it directly as a method argument:

using System;

public class Example
{
   static void Main()  
   {  
      ShowValue(x => x * x);  
   }  

   private static void ShowValue(Func<int,int> op)
   {
      for (int ctr = 1; ctr <= 5; ctr++)
         Console.WriteLine("{0} x {0} = {1}",
                           ctr,  op(ctr));
   }
}
// The example displays the following output:
//   1, 1 x 1 = 1
//   2, 2 x 2 = 4
//   3, 3 x 3 = 9
//   4, 4 x 4 = 16
//   5, 5 x 5 = 25

Expression lambdas

A lambda expression with an expression on the right side of the => operator is called an expression lambda. Expression lambdas are used extensively in the construction of expression trees. An expression lambda returns the result of the expression and takes the following basic form:

(input parameters) => expression

The parentheses are optional only if the lambda has one input parameter; otherwise they are required. Specify zero input parameters with empty parentheses:

Action line = () => Console.WriteLine();

Two or more input parameters are separated by commas enclosed in parentheses:

Func<int,int,bool> testEquality = (x, y) => x == y;  // test for equality

Ordinarily, the compiler uses type inference in determining parameter types. However, sometimes it is difficult or impossible for the compiler to infer the input types. When this occurs, you can specify the types explicitly, as in the following example:

Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;

Note in the previous example that the body of an expression lambda can consist of a method call. However, if you are creating expression trees that are evaluated outside of the .NET Framework, such as in SQL Server or Entity Framework (EF), you should refrain from using method calls in lambda expressions, since the methods may have no meaning outside the context of the .NET implementation. If you do choose to use method calls in this case, be sure to test them thoroughly to ensure that the method calls can be successfuly resolved.

Statement lambdas

A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

(input parameters) => { statement; }

The body of a statement lambda can consist of any number of statements; however, in practice there are typically no more than two or three.

using System;


public class Example
{
    delegate void TestDelegate(string s);

    public static void Main()
    {
       TestDelegate test = n => { string s = n + " " + "World"; Console.WriteLine(s); };  
       test("Hello");
    }
}
// The example displays the following output:
//     Hello World

Statement lambdas, like anonymous methods, cannot be used to create expression trees.

Async lambdas

You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. For example, the example calls a ShowSquares method that executes asynchronously.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Begin().Wait();
   }

   private static async Task Begin()
   {
      for (int ctr = 2; ctr <= 5; ctr++) {
         var result = await ShowSquares(ctr);
         Console.WriteLine("{0} * {0} = {1}", ctr, result);
      }
   }

   private static async Task<int>  ShowSquares(int number)
   {
         return await Task.Factory.StartNew( x => (int)x * (int)x, number);
   } 
}

For more information about how to create and use async methods, see Asynchronous programming with async and await.

Lambda expressions and tuples

Starting with C# 7.0, the C# language provides built-in support for tuples. You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. In some cases, the C# compiler uses type inference to determine the types of tuple components.

You define a tuple by enclosing a comma-delimited list of its components in parentheses. The following example uses tuple with 5 components to pass a sequence of numbers to a lambda expression, which doubles each value and returns a tuple with 5 components that contains the result of the multiplications.

using System;

public class Example
{
    public static void Main()
    {
        var numbers = (2, 3, 4, 5, 6);
        Func<(int, int, int, int, int), (int, int, int, int, int)> doubleThem = (n) => (n.Item1 * 2, n.Item2 * 2, n.Item3 * 2, n.Item4 * 2, n.Item5 * 2);
        var doubledNumbers = doubleThem(numbers);

        Console.WriteLine("The set {0} doubled: {1}", numbers, doubledNumbers);
        Console.ReadLine();
    }
}
// The example displays the following output:
//    The set (2, 3, 4, 5, 6) doubled: (4, 6, 8, 10, 12)

Ordinarily, the fields of a tuple are named Item1, Item2, etc. You can, however, define a tuple with named components, as the following example does.

using System;

public class Example
{
    public static void Main()
    {
        var numbers = (2, 3, 4, 5, 6);
        Func<(int n1, int n2, int n3, int n4, int n5), (int, int, int, int, int)> doubleThem = (n) => (n.n1 * 2, n2 * 2, n.n3 * 2, n.n4 * 2, n.n5 * 2);
        var doubledNumbers = doubleThem(numbers);

        Console.WriteLine("The set {0} doubled: {1}", numbers, doubledNumbers);
        Console.ReadLine();
    }
}
// The example displays the following output:
//    The set (2, 3, 4, 5, 6) doubled: (4, 6, 8, 10, 12)

For more information on support for tuples in C#, see C# Tuple types.

Lambdas with the standard query operators

LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. These delegates use type parameters to define the number and type of input parameters, and the return type of the delegate. Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. For example, consider the Func<TResult> delegate, whose syntax is:

public delegate TResult Func<TArg, TResult>(TArg arg);

The delegate can be instantiated with code like the following

Func<int, bool> func = (x) => x == 5; 

where int is an input parameter, and bool is the return value. The return value is always specified in the last type parameter. When the following Func delegate is invoked, it returns true or false to indicate whether the input parameter is equal to 5:

Console.WriteLine(func(4));      // Returns "False".     

You can also supply a lambda expression when the argument type is an Expression<TDelegate>, for example in the standard query operators that are defined in the Queryable type. When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree. The following example uses the System.Linq.Enumerable.Count standard query operator.

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };  
int oddNumbers = numbers.Count(n => n % 2 == 1);  
Console.WriteLine("There are {0} odd numbers in the set", oddNumbers);
// Output: There are 5 odd numbers in the set

The compiler can infer the type of the input parameter, or you can also specify it explicitly. This particular lambda expression counts those integers (n) that, when divided by two, have a remainder of 1.

The following example produces a sequence that contains all elements in the numbers array that precede the 9, because that's the first number in the sequence that doesn't meet the condition.

var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
foreach (var number in firstNumbersLessThan6)
   Console.Write("{0}     ", number);  
// Output: 5     4     1     3

The following example specifies multiple input parameters by enclosing them in parentheses. The method returns all the elements in the numbers array until it encounters a number whose value is less than its ordinal position in the array.

 var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
foreach (var number in firstSmallNumbers)
   Console.Write("{0}     ", number);
 // Output: 5     4

Type inference in lambda expressions

When writing lambdas, you often do not have to specify a type for the input parameters because the compiler can infer the type based on the lambda body, the parameter types, and other factors, as described in the C# Language Specification. For most of the standard query operators, the first input is the type of the elements in the source sequence. If you are querying an IEnumerable<Customer>, then the input variable is inferred to be a Customer object, which means you have access to its methods and properties:

customers.Where(c => c.City == "London");

The general rules for type inference for lambdas are:

  • The lambda must contain the same number of parameters as the delegate type.

  • Each input argument in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

Note that lambda expressions in themselves do not have a type because the common type system has no intrinsic concept of "lambda expression." However, it is sometimes convenient to speak informally of the "type" of a lambda expression. In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

Variable Scope in Lambda Expressions

Lambdas can refer to outer variables (see Anonymous methods) that are in scope in the method that defines the lambda function, or in scope in the type that contains the lambda expression. Variables that are captured in this manner are stored for use in the lambda expression even if the variables would otherwise go out of scope and be garbage collected. An outer variable must be definitely assigned before it can be consumed in a lambda expression. The following example demonstrates these rules.

using System;

delegate bool D();  
delegate bool D2(int i);  
  
class Test  
{  
    D del;  
    D2 del2;  
    public void TestMethod(int input)  
    {  
        int j = 0;  
        // Initialize the delegates with lambda expressions.  
        // Note access to 2 outer variables.  
        // del will be invoked within this method.  
        del = () => { j = 10;  return j > input; };  
  
        // del2 will be invoked after TestMethod goes out of scope.  
        del2 = (x) => {return x == j; };  
  
        // Demonstrate value of j:  
        // Output: j = 0   
        // The delegate has not been invoked yet.  
        Console.WriteLine("j = {0}", j);        // Invoke the delegate.  
        bool boolResult = del();  
  
        // Output: j = 10 b = True  
        Console.WriteLine("j = {0}. b = {1}", j, boolResult);  
    }  
  
    static void Main()  
    {  
        Test test = new Test();  
        test.TestMethod(5);  
  
        // Prove that del2 still has a copy of  
        // local variable j from TestMethod.  
        bool result = test.del2(10);  
  
        // Output: True  
        Console.WriteLine(result);  
    }  
}  
// The example displays the following output:
//      j = 0
//      j = 10. b = True
//      True

The following rules apply to variable scope in lambda expressions:

  • A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • Variables introduced within a lambda expression are not visible in the outer method.

  • A lambda expression cannot directly capture a ref or out parameter from an enclosing method.

  • A return statement in a lambda expression does not cause the enclosing method to return.

  • A lambda expression cannot contain a goto statement, break statement, or continue statement that is inside the lambda function if the jump statement’s target is outside the block. It is also an error to have a jump statement outside the lambda function block if the target is inside the block.

See also

LINQ (Language-Integrated Query)
Anonymous methods
Expression trees