What is a lambda expression and what is an anonymous function?
An anonymous function is basically a function without a name. They are usually passed as a parameter, written inside another method and they don’t have a name.
A lambda expression is a simple way of creating an anonymous function. Instead of writing a new, verbose
method many lines away from its usage, we can use the lambda operator =>
. This allows us
to write methods in a simple form where they are used. The following example illustrates the simplicity
of lambda expressions:
public static IEnumerable<DateTime> GetDatesInFutureWithLambdaExpression(IEnumerable<DateTime> dates)
{
return dates.Where(x => x > DateTime.Now); // lambda expression
}
Instead of the simple expression lambda syntax, we could also use statement syntax which allows multiple
statements but requires curly brackets, semicolons and an explicit return
statement:
// expression syntax
return dates.Where(x => x > DateTime.Now);
// statement syntax
return dates.Where(x => {
DateTime now = DateTime.Now;
return (x > now);
});
Before we had lambda expressions, we had the delegate
keyword. Using the delegate
keyword leads to the same result, but with more code (delegate
, curly brackets, return
and semicolon):
return dates.Where(delegate (DateTime x) { return x > DateTime.Now; });
We can also extract the function, give it a name and pass a reference to the method to achieve the same result:
public static IEnumerable<DateTime> GetDatesInFutureWithMethodReference(IEnumerable<DateTime> dates)
{
return dates.Where(IsDateInFuture);
}
private static bool IsDateInFuture(DateTime date)
{
return (date > DateTime.Now);
}
The types of parameters and the return value are usually inferred, nevertheless it is possible to define the types explicitly:
return dates.Where(bool (DateTime x) => x > DateTime.Now);
Attributes are also supported, although they have no effect on the anonymous functions. They are only useful with expressions (see next chapter).
Func<string, bool> throwingFunc = [DoesNotReturn] ([Description("This is x.")] x) => throw new Exception();
What are other uses for lambda expressions?
Lambda expressions can also be used to create Expression
objects. These objects contain the meta
data of the code inside the lambda expression and are used for components like Entity Framework where C#
expressions are parsed and converted to SQL. The expressions can also be converted to an anonymous function if required.
LambdaExpression expression = bool (int value1, int value2) => value1 == value2;
Console.WriteLine(expression.Parameters[0].Name); // value1
Console.WriteLine(expression.Parameters[1].Name); // value2
Console.WriteLine(expression.Body); // (value1 == value2)
Console.WriteLine(expression.ReturnType.Name); // Boolean
How are lambda expressions compiled to anonymous functions?
Lambda expressions are a high-level feature and are lowered to simpler structures which can be compiled
to IL code. The lowered C# code for the GetDatesInFutureWithLambdaExpression
method above
looks like this:
private static IEnumerable<DateTime> GetDatesInFutureWithLambdaExpression(IEnumerable<DateTime> dates)
{
return Enumerable.Where(dates,
<>c.<>9__1_0 ?? (<>c.<>9__1_0 = new Func<DateTime, bool>(<>c.<>9.<GetDatesInFutureWithLambdaExpression>b__1_0)));
}
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
[Nullable(0)]
public static readonly <>c <>9 = new <>c();
[Nullable(0)]
public static Func<DateTime, bool> <>9__1_0;
internal bool <GetDatesInFutureWithLambdaExpression>b__1_0(DateTime x)
{
return x > DateTime.Now;
}
}
The extension method call to Where
was replaced with a regular call to Enumerable.Where
.
This has nothing to do with the lambda expression. This is just another C# feature lowered to a simpler structure.
The lambda expression, the second parameter on the call to Where
, is completely gone and replaced with
a cryptic instruction:
<>c.<>9__1_0 ?? (<>c.<>9__1_0 = new Func<DateTime, bool>(<>c.<>9.<GetDatesInFutureWithLambdaExpression>b__1_0))
This is valid C# syntax, but the names with the angle brackets are actually invalid in C# but valid in IL.
By using these names, name collisions can be avoided. The code returns a reference to the Func
property in the generated class (and initializes the reference if required). The method contains the code we
used in our lambda expression. Right now, this class could also have been a static method but apparently, an
instance method does
perform better
in this case.
A lambda expression can also capture state values from outside. We can replace the call to DateTime.Now
with a reference to a new parameter:
public static IEnumerable<DateTime> GetDatesInFutureWithLambdaExpression(IEnumerable<DateTime> dates, DateTime now)
{
return dates.Where(x => x > now);
}
The result is an updated generated class that now lacks a cache of a Func
property but got a field for
the DateTime
value instead. The lambda expression got replaced by a call to an instance of that class.
The now
variable is directly set on the instance:
[NullableContext(1)]
public static IEnumerable<DateTime> GetDatesInFutureWithLambdaExpression(IEnumerable<DateTime> dates, DateTime now)
{
<>c__DisplayClass1_0 <>c__DisplayClass1_ = new <>c__DisplayClass1_0();
<>c__DisplayClass1_.now = now;
return Enumerable.Where(dates, new Func<DateTime, bool>(<>c__DisplayClass1_.<GetDatesInFutureWithLambdaExpression>b__0));
}
[CompilerGenerated]
private sealed class <>c__DisplayClass1_0
{
public DateTime now;
internal bool <GetDatesInFutureWithLambdaExpression>b__0(DateTime x)
{
return x > now;
}
}
This concept is called closure, and the generated class is called a Display Class. This structure preserves the
state, and therefore the method can be executed later. As I described in
my last article about IEnumerable
,
this code gets executed later and at a different place (for example when ToList()
is called). With
this structure, we still use the now
value from the parameter.
How do async lambda expressions work?
Async lambda expressions are possible although they should be used with care. An asynchronous lambda usually
only makes sense when the recipient of the lambda supports asynchronous functions which means it uses a variation
of Func<Task>
. In the following example, Task.Delay
is called but then ignored
because it is not awaited in the DoForEach
method (like an async void
method):
public static async Task TestAsync()
{
IEnumerable<int> delaySeconds = [1, 2, 3];
delaySeconds.DoForEach(async x => await Task.Delay(TimeSpan.FromSeconds(x)));
}
private static void DoForEach<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (T item in items)
{
action(item);
}
}
An asynchronous variation of the DoForEach
method would look like this and would solve the problem:
public static async Task TestAsync()
{
IEnumerable<int> delaySeconds = [1, 2, 3];
await delaySeconds.DoForEachAsync(async x => await Task.Delay(TimeSpan.FromSeconds(x)));
}
private static async Task DoForEachAsync<T>(this IEnumerable<T> items, Func<T, Task> action)
{
foreach (T item in items)
{
await action(item);
}
}
I saw the usage of async lambda expression in initialization code for web applications. At one place an
Action
was required and an async lambda was used. In the best case, the initialization still
works and in the worst case it fails without anyone noticing (fire and forget). Because it runs asynchronously
without awaiting, we also don’t know if the initialization is finished before the first requests are handled.
Can lambda expressions be marked as static?
It took me many years to realize that lambda expressions can be marked as static
which is
possible since C# 9 in the year 2020:
return dates.Where(static x => x > DateTime.Now);
Many lambda expressions in LINQ queries could probably be marked as static. But this would not change the result in most cases. It isn’t even guaranteed that the static definition is emitted in the final code according to the specification. And, as already mentioned above, an instance method can be better than a static method in some cases. The real value of the static keyword in this context is to make sure that no state is captured. Therefore, this is more a hint for programmers than a possible direct performance optimization.
Can ref, out and params modifiers be applied to lambda expressions?
Applying ref
, out
and params
is possible. For those cases we can’t use
the generic Func<T>
and Action
delegates but can declare our own. The types of
the parameters can’t be inferred at the moment (this will probably change in the next C# release).
public delegate void RefDelegate(ref int value);
public delegate void OutDelegate(out int value);
public delegate void ParamsDelegate(params int[] values);
RefDelegate example1 = (ref int value) => value = value++;
OutDelegate example2 = (out int value) => value = 1;
ParamsDelegate example3 = (params int[] value) => Console.WriteLine(value.Length);
Conclusion
Lambda expressions are very powerful and handle a lot of complicated logic with just an arrow. But lambda
expressions have also features that are rarely used, like static
and parameter modifiers.
I can’t imagine C# without lambda expressions and anonymous functions.