C# – Lambda Expression

Lambda Expression

Unnamed method written in place of a delegate instance. Compiler converts lambda expression either to delegate instance or expression tree. Lambda expressions are used most commonly with the Func and Action delegates.

Lambda Expression
(parameters) => expression-or-statement-block
Note: If there is only one parameter than parentheses can be omitted.

delegate int Transformer (int i);

Assigns lambda expression as :

Transformer sqr = x => x * x;

Explicitly Specifying Lambda Parameter Types

Sometimes the compiler can infer the type of parameter but if this is not the case then you must explicitly define the parameter.

delegate int Transformer (int i);

static void Main()
{
	Transformer sqr = x => x * x;
	Console.WriteLine (sqr(3));    // 9
	
	// Using a statement block instead:
	Transformer sqrBlock = x => { return x * x; };
	Console.WriteLine (sqr(3));
	
	// Using a generic System.Func delegate:
	Func sqrFunc = x => x * x;
	Console.WriteLine (sqrFunc(3));
	
	// Using multiple arguments:
	Func totalLength = (s1, s2) => s1.Length + s2.Length;
	int total = totalLength ("hello", "world");
	total.Dump ("total");
	
	// Explicitly specifying parameter types:
	Func sqrExplicit = (int x) => x * x;
	Console.WriteLine (sqrFunc(3));
}

Capturing Outer Variables

Outer variable referenced by lambda expression are called captured variables. Expression that captures variables is called a closure.

Captured variables are evaluated when the delegate is actually invoked and not when that variable are captured.

int factor = 2;
Func multiplier = n => n * factor;
Console.WriteLine (multiplier (3));           // 6

// Captured variables are evaluated when the delegate is invoked, not when the variables were captured:

factor = 10;
Console.WriteLine (multiplier (3));           // 30

// Lambda expressions can themselves update captured variables:

int seed = 0;
Func natural = () => seed++;
Console.WriteLine (natural());           // 0
Console.WriteLine (natural());           // 1
Console.WriteLine (seed);                // 2

Captured variables have their lifetimes extended to that of the delegate.

// Captured variables have their lifetimes extended to that of the delegate:

static Func Natural()
{
	int seed = 0;
	return () => seed++;	  // Returns a closure
}

static void Main()
{
	Func natural = Natural();
	Console.WriteLine (natural());      // 0
	Console.WriteLine (natural());      // 1
}

A local variable instantiated within a lambda expression is unique per invocation of the delegate instance

static Func Natural()
{    
	return() => { int seed = 0; return seed++; };
}

static void Main()
{
	Func natural = Natural();
	Console.WriteLine (natural());           // 0
	Console.WriteLine (natural());           // 0
}

Capturing iteration variables

When you capture the iteration variable in a for-loop, C# treats that variable as though it was declared outside the loop. This means that the same variable is captured in each iteration.

	Action[] actions = new Action[3];

	for (int i = 0; i < 3; i++)
		actions [i] = () => Console.Write (i);
	
	foreach (Action a in actions) a();     // 333 (instead of 123)

Each closure captures the same variable , i . When the delegates are later invoked, each delegate see i’s value at the time of invocation.

	Action[] actions = new Action[3];
	int i = 0;
	actions[0] = () => Console.Write (i);
	i = 1;
	actions[1] = () => Console.Write (i);
	i = 2;
	actions[2] = () => Console.Write (i);
	i = 3;
	foreach (Action a in actions) a();    // 333

Workaround

Assign the iteration variable to a local variable that is scoped inside the loop. Local variable will be created on every iteration therefore the closure captures different variable.

Action[] actions = new Action[3];

for (int i = 0; i < 3; i++)
{
	int loopScopedi = i;
	actions [i] = () => Console.Write (loopScopedi);
}

foreach (Action a in actions) a();     // 012

Anonymous Methods

Anonymous methods capture variables in the same way as lambda expression.

delegate int Transformer (int i);
Transformer sqr = delegate (int x) { return x * x ;};

==equals
Transformer sqr = (int x) => { return x * x;};

==equals
Transformer sqr = x => x * x;
delegate int Transformer (int i);

static void Main()
{
	// This can be done more easily with a lambda expression:
	Transformer sqr = delegate (int x) { return x * x; };
	Console.WriteLine (sqr(3));                            // 9
}

// A unique feature of anonymous methods is that you can omit the parameter declaration entirely — even 
// if the delegate expects them. This can be useful in declaring events with a default empty handler:
public static event EventHandler Clicked = delegate { };
// because it avoids the need for a null check before firing the event.

// The following is also legal:
static void HookUp()
{
	Clicked += delegate { Console.WriteLine ("clicked"); };   // No parameters
}