Delegates, Func, Action and lambdas
LINQ is built on one idea: passing a piece of code into a method. A delegate is a type-safe reference to a method. You rarely declare your own, because .NET ships generic ones: Func<T, TResult> takes arguments and returns a value (the last type argument is the return type), Action<T> takes arguments and returns nothing, and Predicate<T> is a Func<T, bool> by another name. A lambda — x => x * x — is the short way to write the method on the spot.
Func<int, int> square = x => x * x;
Func<int, int, int> add = (a, b) => a + b;
Action<string> shout = msg => Console.WriteLine(msg.ToUpper() + "!");
Predicate<int> isEven = n => n % 2 == 0;
Console.WriteLine(square(5));
Console.WriteLine(add(2, 3));
shout("hello");
Console.WriteLine(isEven(7));
// a method group works anywhere a matching delegate is expected
Func<string, int> parse = int.Parse;
Console.WriteLine(parse("42") + 1);
Console.WriteLine(Apply(10, square));
Console.WriteLine(Apply(10, n => n - 1));
static int Apply(int value, Func<int, int> f) => f(value);25
5
HELLO!
False
43
100
9Write a Func<string, bool> called isLong that is true for strings longer than 5 characters, and pass it into a method CountWhere(string[] words, Func<string, bool> test).
Closures: lambdas remember variables
var counter = MakeCounter();
Console.WriteLine(counter());
Console.WriteLine(counter());
Console.WriteLine(counter());
int threshold = 10;
Func<int, bool> big = n => n > threshold;
threshold = 100; // the lambda sees the NEW value
Console.WriteLine(big(50));
static Func<int> MakeCounter()
{
int count = 0;
return () => ++count; // count lives on after MakeCounter returns
}1
2
3
FalseA lambda captures the variable itself, not a copy of its value. That is why big(50) compares against 100.
CS1593: calling a delegate with the wrong number of arguments
Func<int, int> square = x => x * x;
Console.WriteLine(square(3, 4));Program.cs(2,19): error CS1593: Delegate 'Func<int, int>' does not take 2 argumentsFunc<int, int> is one int in, one int out. The last type argument is always the return type, so it takes a single argument. Beginners often read it as "two ints in".
Use Func<int, int, int> for two inputs and one output.
Func<int, int, int> multiply = (a, b) => a * b;
Console.WriteLine(multiply(3, 4));CS8917: var with a lambda whose parameter type is unknown
var doubler = x => x * 2;
Console.WriteLine(doubler(4));Program.cs(1,15): error CS8917: The delegate type could not be inferred.Since C# 10 a lambda can have a "natural type" so var works — but only if the compiler can see the parameter types. Here x could be anything.
Type the parameter in the lambda, or declare the variable with an explicit Func.
var doubler = (int x) => x * 2; // Func<int, int>
Console.WriteLine(doubler(4));