User Tools

Site Tools


notes:csharp:lambda

Lambda Expressions in C#

A lambda expression is a shorthand syntax for creating anonymous inline methods. Lambda expressions can be used whenever a delegate is used.

At compile time a lambda expression becomes either:

  • an instance of a delegate
  • or, an expression tree

Lambdas use the => notation, which can be read as “becomes” or “for which”.

Examples

Func<int, int, int> f1 = (x, y) => x + y;
int n1 = f1(2, 2); // n1 == 4
 
Func<int, int, int> f2 = (x, y) => x * y;
int n2 = f2(7, 6); // n2 == 42
 
Func<int, int, string> f3 = (x, y) => (x * y).ToString();
string s1 = f3(8, 11); // s1 == "88"

Equivalent lambda expressions:

// A lambda expression with a statement block.
Func<string, int> f1 = (string str) => 
{ 
    return str.Length; 
};
 
// You can omit the braces if there is a single expression.
Func<string, int> f2 = (string str) => str.Length;
 
// You can omit the type of the parameter if the type can be implicitly inferred.
Func<string, int> f3 = (str) => str.Length;
 
// You can omit the parentheses if there is only one implicitly typed parameter.
Func<string, int> f4 = str => str.Length;
 
// The above lambda expressions are equivalent to the following code that creates 
// a delegate instance using an anonymous method:
Func<string,int> f5 = delegate(string str) { return str.Length; };

Provide delegate instances for the ForEach, FindAll, and Sort methods as lambda expressions. ForEach, FindAll, and Sort are the extension methods on List<T>:

var numbers = new List<int> { 8, 5, 13, 4 };
 
Action<int> print = n => Console.Write("{0} ", n);
 
// The ForEach method takes an Action<T>.
numbers.ForEach(print); // 8 5 13 4
 
// The FindAll method takes a Predicate<T>.
numbers.FindAll(n => n < 8).ForEach(print); // 5 4
 
// The Sort method takes a Comparison<T>.
numbers.Sort((n1, n2) => n1.CompareTo(n2));
numbers.ForEach(print); // 4 5 8 13

Implement event handlers using lambda expressions:

// btnSave is a button on a page.
public InitPage()
{
    btnSave.Loaded += (sender, args) =>
    {
        // ...
    };
 
    btnSave.Click += (sender, args) => SaveData("Click", sender, args);
}
 
void SaveData(string eventName, object sender, EventArgs args)
{
    // ...
}

Closures

A lambda expression that captures variables is called a closure. A closure gives an ability for a lambda expression to interact with an environment beyond the parameters provided to it. A captured variable lives for at least as long as any function referring to it.

Example: Access a variable 'a' outside of the block of a lambda expression:

int a = 8;
 
Func<int, int> f = x => x + a;
 
Console.WriteLine(f(5)); // 5+8=13
a = 10;
Console.WriteLine(f(5)); // 5+10=15
Add(f);
...
private void Add(Func<int, int> q)
{
    // 'a' is alive here too
    Console.WriteLine(q(7)); // 7+10=17
}

Example: Return a closure from a method.

static Func<int> Counter()
{
    // n is a captured variable.
    int n = 0;
    return () => ++n; // returns a closure
}
...
Func<int> f = Counter();
Console.WriteLine(Counter()); // 1
Console.WriteLine(Counter()); // 2

Example: Outer, local, captured, and uncaptured variables:

// 'o' is an outer uncaptured variable.
// The variable is 'outer' because the anonymous method is declared within its scope.
int o = 0;
 
// 'a' is an outer captured variable because the anonymous method refers to it.
int a = 5;
 
Func<int, int> f = x =>
{
    // 'b' is a local variable to the anonymous method.
    // 'b' is created on the stack when the delegate
    // instance (in this case the lambda expression) is executed.
    int b = 3;
 
    return x + a + b;
};
 
// Execute the lambda. This captures the variable 'a' itself rather than merely its value!
Console.WriteLine(f(2)); // 2 + 5 + 3 = 10
 
// Change the value of the captured variable 'a'.
a = 10;
 
// The change of the value of the variable 'a' is visible within the anonymous method.
Console.WriteLine(f(20)); // 20 + 10 + 3 = 33
notes/csharp/lambda.txt · Last modified: 2016/11/28 by admin