C# Lambdas Part 1, a Quick Overview with Examples

Download full source code.

I have tried a few times to write a blog post about using C# lambdas, and each time I have been unable to finish it. Lambdas can be a little confusing at first, and as they get more complex, can be very confusing.

There isn’t one single way to write a lambda or use a lambda, and there isn’t one way to explain them either.

In the past, I have tried to find a way to explain it all in a few sentences, and that is why I couldn’t finish those posts.

In this post, I will show many examples of using lambdas with comments to explain what is going on.

The sample code uses Funcs and Actions, if you are not familiar with them check two posts I wrote on the topic - Actions and Funcs.

A quick overview

This may not be strictly correct, but it is how I understand them. If there is something egregiously wrong, let me know in the comments.

To my mind, a lambda is a way of writing a method, and just like a traditional method, it can take parameters (or none), and return any type (or none).

For example -

(x, y) => x + y; 

Here is how I read it -

  • (x, y) shows that two parameters will be passed to the lambda.
  • => to the right of this is the start of the body of the lambda.
  • x + y is the body of the lambda, it will return the result of x + y. In a one-liner like this, the return is implicit.

The above lambda is the same as this method -

int AddTwoNumbers(int x, int y) // takes two int parameters and returns an int
    return x + y;

Why use a lambda?

A lambda can be invoked or passed to another method, where it will be invoked. That other method can pass parameters into the lambda.

This allows us, the program writers to decide how another method does something. Let me give a couple of concrete examples -

  1. Polly, the resilience framework lets you retry failed HttpClient requests. Of course, Polly can not know what request you want to make, so you pass a Polly Retry method a lambda that makes a HTTP request. The Retry method invokes the lambda you wrote and builds in all the resilience code necessary to retry the request.

    var httpResponseMessage = await retryPolicy.ExecuteAsync(() => httpClient.GetAsync("/"));

  2. If you have a list of numbers and you want to use a LINQ Where on the list, the Where method can take a lambda that YOU define to return only the numbers you want. You could instead write a traditional method to pass to the Where method, but that would be more code than the lambda, and may be less readable.

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var evenNumbersStatementLambda = 
    numbers.Where( // Where is a LINQ method that takes a Func<int, bool> to perform the filter.
        (numFromNumbers) => // The lambda is passed a parameter of type int by the Where method.
            { // this is called a statement lambda, the statement(s) to the right of => are enclosed in braces and has an explicit return statement.
                return numFromNumbers % 2 == 0; // The lambda returns true if the number is even.
            } // this lambda is executed for each number in the numbers list. 

var evenNumbersExpressionLambda = numbers.Where( // the lambda can be written more succinctly.
        numFromNumbers =>  // this is an expression lambda, the statement to the right of => is not enclosed in braces. An explicit return statement is not used. 
        numFromNumbers % 2 == 0 // an explicit return is not needed

More examples

There are many ways to work with and use lambdas. The rest of this post is sample code, with detailed comments explaining what each line does (I don’t repeat comments if the code is identical to something already explained).

I strongly encourage you to step through the code line by line to see what is being executed, what parameters are being passed, and what is being returned.

A .NET Fiddle of the code is available here.

I know the comments are a little difficult to read in this blog post, but please download the attached zip file or open the .NET Fiddle.

  1// For all these examples, step through the code and see what/how the code is executed.
  3// *** Common use case
  4int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  5// this is a common use case for a lambda.
  6// A array of numbers is filtered using a lambda in LINQ where clause.
  8// this is it as a one liner
  9var evenNumbersShorterLambda = numbers.Where(numFromNumbers => numFromNumbers % 2 == 0);
 11var evenNumbers = numbers.Where( // Where takes a Func that has a int as a parameter and returns a bool - Func<int, bool> predicate
 12    (numFromNumbers) => // numFromNumbers is the parameter of the lambda, the Where will pass each number from the array of numbers to the lambda one at a time
 13        {
 14            return numFromNumbers % 2 == 0; // the lambda will return true if the number is even
 15        }
 16    ).ToArray(); // forces execution of the lambda
 19// *** Example 1 *** //
 20Action<string> writeHelloFriend = // Action that takes a single string as a parameter. Actions return nothing.
 21    (friend) => // this is the start of the lambda. (friend) is the parameter that will be passed to the lambda.
 22        Console.WriteLine($"Hello {friend}"); // This is the an expression lambda, it uses the friend parameter and prints to the command line. It returns nothing.
 24writeHelloFriend("Dave"); // Invoke the action, passing "Dave" as the parameter. This will execute the lambda above. "Dave" will be assigned to the friend parameter in the lambda.
 27// *** Example 2 *** //
 28Action<string> writeHelloFriend2 =
 29    (friend) =>
 30    {
 31        Console.WriteLine($"Hello {friend}"); // this is the same as the above, but with a statement lambda. Note the curly braces.
 32    };
 36// *** Example 3 *** //
 37Func<string> sayHello = // sayHello is a Func that returns a string.
 38    () => // the lambda starts here, it takes no parameters, that is what the () means.
 39        "Hello"; // this is the body of the lambda. It returns "Hello". An explicit return is not required for expression lambdas.
 41// Console.WriteLine(..) has many overloads, but one is for a string.
 42// Calling sayHello() will return a string - "Hello".
 46// *** Example 4 *** //
 47Func<string> sayHello2 =
 48    () =>
 49        { // this is the body of a statement lambda. It can have as many statements as needed. But it must have an explicit return (because the Func is defined that way). 
 50            return "Hello";
 51        }; // and it must be enclosed in curly braces.
 55// *** Example 5  *** //
 56Func<string, string> helloFriend = // helloName is a Func that takes a string parameter and returns a string.
 57    (friend) => // (friend) is the name of the parameter that will be passed to the lambda.
 58            $"Hello {friend}"; // this is the body of the lambda. It returns "Hello {friend}".
 59Console.WriteLine(helloFriend("Dave")); // invoke the Func, passing "Dave" as the parameter.
 62// *** Example 6 *** //
 63Func<int, int, string> add = // Add is a Func that takes two int parameters and returns an string.
 64    (x, y) =>  // (x, y) are the parameters that will be passed to the lambda, but they haven't been passed yet.
 65        $"Result of x + y ({x} + {y}) is {x + y}"; // this is the body of the lambda. It will use the x and y parameters, and returns a string.
 66Console.WriteLine(add(2, 3)); // invoke the Func, passing 2 and 3 as the parameters.
 68// If you were to think of the Func as a method instead, it would be:    
 69//          string AddTwoNumbers(int x, int y)
 70// Just like in a lambda, the x and y are the names of parameters that have not been passed yet. That is done by the caller.
 71// And they haven't been used yet, that is done in the body of the method, same in the lambda.
 72// Console.WriteLine(AddTwoNumbers(2, 3)); 
 74// *** Example 7 *** //
 75Func<int, int, string> multiply =
 76    (x, y) =>
 77        $"My multiply Func does this: x * y ({x} * {y}) is {x * y}";
 78// PerformCalculation(..) takes a Func that takes two int parameters and returns a string, PerformCalculation(..) also takes two int parameters.
 79string result1 = PerformCalculation(multiply, 4, 5); // the multiply Func is passed to PerformCalculation along with two parameters that will become the x and y parameters in the lambda body, and the result is assigned to result1.
 83// *** Example 8 *** //
 84// This is the same as the previous example, but the lambda expression is written directly inside PerformCalculation().., satisfying the Func<int, int, string> parameter of PerformCalculation(..).
 85string result2 =
 86    PerformCalculation( // call to PerformCalculation
 87        (a, b) => // first parameter PerformCalculation expects is a Func that takes two int parameters... 
 88                $"Result of a * b ({a} * {b}) is {a * b}",  // ...and returns a string.
 89            3, 4);  // these correspond to the a and b in the parameters of PerformCalculation and will be passed to the lambda body where they become the a and b parameters.
 93// *** Example 9 *** //
 94// This is the same as the previous example, but the lambda does not make use of the a and b parameters.
 95string result3 =
 96    PerformCalculation(
 97        (a, b) =>
 98                $"Not going to calculate anything",
 99            5, 6);  // these correspond to the a and b in the parameters of PerformCalculation, they have to be passed, but the lambda doesn't have to use them.
103// *** Example 10 *** //
104// This is the same as the previous example but discarding parameters that are not going to be used. That is what the _ means
105string result4 =
106    PerformCalculation( // call to PerformCalculation
107        (_, _) => // discard the parameters I'm not going to use. 
108                $"Discarding the parameters",  // Return a string.
109            default(int), default(int));
113// *** Local methods *** //
114// PerformCalculation is a local method that takes a Func that takes two int parameters and returns a string, it also takes two more ints.
115string PerformCalculation(Func<int, int, string> calculation, int a, int b)
117    // The ints, a and b, are passed to the calculation Func. If you F11 through this, you will see the code in the lambda above being executed.
118    return $"The lambda is called in PerformCalculation. {calculation(a, b)}";

I will write a follow up post with a few more examples of lambdas that are a little more complicated.

Download full source code.

comments powered by Disqus