Lambda Expressions
This page was last reviewed on Apr 10, 2024.
Dot Net Perls
Lambda. In C#, lambdas use special syntax. We pass lambda expressions to other methods to specify a behavior that the method uses.
Shows a lambda
Remember that lambdas are just methods. They can always be replaced with class-level methods. We can use types like Action and Func to represent them.
Find example. A common place to use lambdas is with List. Here we use FindIndex, which receives a Predicate method. We specify this as a lambda expression.
Info To the left, we have arguments. The "x" is just a name—we can use any valid name. The result is on the right.
Note We pass lambda expressions as arguments, for sorting or for searching. We use them in queries.
Shows a lambda
using System; using System.Collections.Generic; List<int> elements = new List<int>() { 10, 20, 31, 40 }; // ... Find index of first odd element. int oddIndex = elements.FindIndex(x => x % 2 != 0); Console.WriteLine(oddIndex);
Func. The key part of Func is that it returns a value. It can have zero, or many, arguments. But its invariant is a return value, indicated by the TResult parametric type.
Part 1 We assign a lambda to func1. This lambda receives an int, and returns 1 plus that int value.
Part 2 The lambda here receives an int, and returns 1 plus that int also (just like func1).
Part 3 This lambda is another syntax for the same lambda—it has the same result as the previous 2 Func objects.
Part 4 We see a more verbose syntax—we specify the return keyword, and the argument type of the lambda.
using System; // Part 1: use implicitly-typed lambda expression. // ... Assign it to a Func instance. Func<int, int> func1 = x => x + 1; Console.WriteLine("FUNC1: {0}", func1.Invoke(200)); // Part 2: use lambda expression with statement body. Func<int, int> func2 = x => { return x + 1; }; Console.WriteLine("FUNC2: {0}", func2.Invoke(200)); // Part 3: use formal parameters with expression body. Func<int, int> func3 = (int x) => x + 1; Console.WriteLine("FUNC3: {0}", func3.Invoke(200)); // Part 4: use parameters with a statement body. Func<int, int> func4 = (int x) => { return x + 1; }; Console.WriteLine("FUNC4: {0}", func4.Invoke(200));
FUNC1: 201 FUNC2: 201 FUNC3: 201 FUNC4: 201
Func, 2 arguments. We can use a lambda expression that has 2 arguments. We assign the lambda here to a Func that receives 2 ints, and returns an int value as well.
using System; // Use multiple parameters. Func<int, int, int> func = (x, y) => x * y; // ... No need to call Invoke(), just call lambda directly. Console.WriteLine("RESULT: {0}", func(20, 2));
Action. The Action encapsulates a function that receives any number of parameters, but returns no value. It matches a void method.
using System; // Use no parameters in a lambda expression. // ... This is an Action instance. Action value = () => Console.WriteLine("Hi, friend"); value.Invoke();
Hi, friend
Delegate. The delegate keyword denotes an anonymous function. After this keyword, we use a formal parameter list. We can omit the list if there are no parameters.
Part 1 This delegate (an anonymous function) has 1 argument of type int. It returns an int as well.
Part 2 The delegate here has no arguments. It just returns an int. It is assigned to a Func object.
using System; // Part 1: use delegate method expression. Func<int, int> test1 = delegate (int x) { return x + 1; }; Console.WriteLine(test1.Invoke(10)); // Part 2: use delegate expression with no parameter list. Func<int> test2 = delegate { return 1 + 1; }; Console.WriteLine(test2.Invoke());
11 2
Predicate. Here we use this type with an int parameter. With a lambda expression, we specify that the function returns true if the argument is equal to 5.
Note In this program, the Invoke method is used to show that the Predicate works as expected.
using System; // Specify a Predicate instance. Predicate<int> predicate = value => value == 5; Console.WriteLine(predicate.Invoke(4)); Console.WriteLine(predicate.Invoke(5));
False True
Lambda array. Suppose we want to call a function in an array based on an index. We can create an array of Funcs and then create each element with a lambda expression.
Here We create a lookup table of 3 functions, and call them based on an index.
Result The output shows that the offsets 100, 200 and 300 are added in an alternating way to the for-iteration value.
using System; // Create an array of lambdas. var lookup = new Func<int, int>[] { a => a + 100, a => a + 200, a => a + 300 }; // Call the lambdas in the lookup table and print the results. for (int i = 0; i < 10; i++) { int result = lookup[i % 3](i); Console.WriteLine(result); }
100 201 302 103 204 305 106 207 308 109
Expression-bodied methods. A method can be specified with lambda expression syntax. We provide a method name, and the method is compiled like a lambda. A "return" statement is implicit.
class Program { static int TreeBranches(int branches, int trunks) => (branches * trunks); static void Main() { // Call the expression-bodied method. System.Console.WriteLine(TreeBranches(10, 2)); } }
Benchmark, delegates. We benchmarked a lambda against an anonymous method, one using the delegate keyword. We used the functions as arguments to the Count() extension.
Version 1 This version of the code uses a lambda expression. It passes the lambda to the Count() method.
Version 2 Here we use a delegate reference instead of a directly-specified lambda expression as the argument to Count.
Result On Windows .NET 8 in 2024, the delegate method version seems to be faster than a lambda Func call.
using System; using System.Diagnostics; using System.Linq; const int _max = 10000000; int[] array = { 1 }; Func<int, bool> delegateVersion = delegate (int argument) { return argument == 1; }; // Version 1: use lambda expression for Count. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { if (array.Count(element => element == 1) == 0) { return; } } s1.Stop(); // Version 2: use delegate for Count. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { if (array.Count(delegateVersion) == 0) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));
25.97 ns lambda expression 7.81 ns delegate
Lambdas have unique syntactic rules. We had some help from the C# specification itself. We used lambdas with zero, one or many arguments, and with a return value.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Apr 10, 2024 (simplify).
© 2007-2024 Sam Allen.