Lambda. 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 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.
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.
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;
class Program
{
static void Main()
{
// 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
Action. The Action encapsulates a function that receives any number of parameters, but returns no value. It matches a void method. This guy is a solitary character.
using System;
class Program
{
static void Main()
{
// 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;
class Program
{
static void Main()
{
// Part A: use delegate method expression.
Func<int, int> test1 = delegate (int x) { return x + 1; };
Console.WriteLine(test1.Invoke(10));
// Part B: 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.
Detail In this program, the Invoke method is used to show that the Predicate works as expected.
using System;
class Program
{
static void Main()
{
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;
class Program
{
static void Main()
{
// 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));
}
}20
Cannot convert lambda error. The right-hand part of a lambda expression must have a conversion to the required result type. Here, Array.Find requires a bool, but we just have an int.
Important Look at the two parts of the lambda separated by the arrow. The variable name should be present on both sides.
using System;
class Program
{
static void Main()
{
var array = new int[] { 10, 20, 30 };
var result = Array.Find(array, element => 20);
}
}Error CS1662
Cannot convert lambda expression to intended delegate type because some of the return types
in the block are not implicitly convertible to the delegate return type
Error CS0029
Cannot implicitly convert type 'int' to 'bool'
Error, continued. To fix the lambda expression, we must make sure the right-hand part of the lambda is correct. Here is the fix and a possible output of the program.
Tip We changed the right-hand side of the lambda to have an equality expression, so it now evaluates to a bool correctly.
using System;
class Program
{
static void Main()
{
var array = new int[] { 10, 20, 30 };
var result = Array.Find(array, element => element == 20);
Console.WriteLine(result);
}
}20
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 .NET 5 in 2021, the delegate method version seems to be faster than a lambda Func call.
using System;
using System.Diagnostics;
using System.Linq;
class Program
{
const int _max = 10000000;
static void Main()
{
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"));
}
}27.68 ns lambda expression
22.22 ns delegate
Benchmark, lambda cache. Avoiding repeat work is a key to program optimization. Suppose we want to call a method like Array.Sort with a lambda argument.
And We may need to reuse the lambda many times. We can store the lambda in a variable like a Comparison, and reuse it.
Version 1 In this version of the code, we call a lambda 3 times, but specify it as an argument expression 3 times.
Version 2 Here we cache the lambda expression in a local variable. So only 1 lambda is used.
Result On .NET 5 for Linux, using a cached lambda in repeat invocations is faster. Lambdas overall have been optimized.
using System;
using System.Diagnostics;
class Program
{
const int _max = 1000000;
static void Main()
{
int[] values = { 0, 10 };
// Version 1: use lambda directly in each Array.Sort call.
var s1 = Stopwatch.StartNew();
for (int i = 0; i < _max; i++)
{
Array.Sort(values, (a, b) => (b.CompareTo(a)));
Array.Sort(values, (a, b) => (b.CompareTo(a)));
Array.Sort(values, (a, b) => (b.CompareTo(a)));
}
s1.Stop();
// Version 2: store lambda as local, then reuse it for each Array.Sort call.
var s2 = Stopwatch.StartNew();
for (int i = 0; i < _max; i++)
{
Comparison<int> temp = (a, b) => (b.CompareTo(a));
Array.Sort(values, temp);
Array.Sort(values, temp);
Array.Sort(values, temp);
}
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"));
}
}80.90 ns Array.Sort, 3 separate lambdas
77.30 ns Array.Sort, 1 cached lambda
Anonymous function. This term includes the syntax for lambdas and delegates. Lambda expressions are a modernized improvement over (and replacement for) the delegate syntax.
Comparison. This type is specifically used to compare objects. It is useful when calling the List.Sort or Array.Sort methods. It can be used with any object type.
Detail Using methods such as List.Sort or Array.Sort (with a Comparison) is often faster than using LINQ to sort on a property.
Events. Like any other method, events can be specified as lambda expressions. With events, many event handlers are called when a certain thing happens. This can simplify some programs.
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 Nov 10, 2023 (simplify).