In C#, lambdas use special syntax. We pass lambda expressions to other methods to specify a behavior that the method uses.
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
exampleA common place to use lambdas is with List
. Here we use FindIndex
, which receives a Predicate
method. We specify this as a lambda expression.
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);2
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.
int
, and returns 1 plus that int
value.int
, and returns 1 plus that int
also (just like func1).Func
objects.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 argumentsWe 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));RESULT: 40
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
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.
int
. It returns an int
as well.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.
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
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.
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
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)); } }20
We benchmarked a lambda against an anonymous method, one using the delegate keyword. We used the functions as arguments to the Count()
extension.
Count()
method.Count
.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.