Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

Chapter 84: Lambda expressions

Section 84.1: Lambda Expressions as Shorthand for Delegate Initialization

public delegate int ModifyInt(int input); ModifyInt multiplyByTwo = x => x * 2;

The above Lambda expression syntax is equivalent to the following verbose code:

public delegate int ModifyInt(int input);

ModifyInt multiplyByTwo = delegate(int x){ return x * 2;

};

Section 84.2: Lambda Expression as an Event Handler

Lambda expressions can be used to handle events, which is useful when:

The handler is short.

The handler never needs to be unsubscribed.

A good situation in which a lambda event handler might be used is given below:

smtpClient.SendCompleted += (sender, args) => Console.WriteLine("Email sent");

If unsubscribing a registered event handler at some future point in the code is necessary, the event handler expression should be saved to a variable, and the registration/unregistration done through that variable:

EventHandler handler = (sender, args) => Console.WriteLine("Email sent");

smtpClient.SendCompleted += handler; smtpClient.SendCompleted -= handler;

The reason that this is done rather than simply retyping the lambda expression verbatim to unsubscribe it (-=) is that the C# compiler won't necessarily consider the two expressions equal:

EventHandler handlerA = (sender, args) => Console.WriteLine("Email sent");

EventHandler handlerB = (sender, args) => Console.WriteLine("Email sent");

Console.WriteLine(handlerA.Equals(handlerB)); // May return "False"

Note that if additional statements are added to the lambda expression, then the required surrounding curly braces may be accidentally omitted, without causing compile-time error. For example:

smtpClient.SendCompleted += (sender, args) => Console.WriteLine("Email sent"); emailSendButton.Enabled = true;

This will compile, but will result in adding the lambda expression (sender, args) => Console.WriteLine("Email sent"); as an event handler, and executing the statement emailSendButton.Enabled = true; immediately. To fix this, the contents of the lambda must be surrounded in curly braces. This can be avoided by using curly braces from the start, being cautious when adding additional statements to a lambda-event-handler, or surrounding the lambda in round brackets from the start:

GoalKicker.com – C# Notes for Professionals

477

smtpClient.SendCompleted += ((sender, args) => Console.WriteLine("Email sent"));

//Adding an extra statement will result in a compile-time error

Section 84.3: Lambda Expressions with Multiple Parameters or No Parameters

Use parentheses around the expression to the left of the => operator to indicate multiple parameters.

delegate int ModifyInt(int input1, int input2); ModifyInt multiplyTwoInts = (x,y) => x * y;

Similarly, an empty set of parentheses indicates that the function does not accept parameters.

delegate string ReturnString();

ReturnString getGreeting = () => "Hello world.";

Section 84.4: Lambdas can be emitted both as `Func` and `Expression`

Assuming the following Person class:

public class Person

{

public string Name { get; set; } public int Age { get; set; }

}

The following lambda:

p => p.Age > 18

Can be passed as an argument to both methods:

public void AsFunc(Func<Person, bool> func)

public void AsExpression(Expression<Func<Person, bool>> expr)

Because the compiler is capable of transforming lambdas both to delegates and Expressions.

Obviously, LINQ providers rely heavily on Expressions (exposed mainly through the IQueryable<T> interface) in order to be able to parse queries and translate them to store queries.

Section 84.5: Put Multiple Statements in a Statement Lambda

Unlike an expression lambda, a statement lambda can contain multiple statements separated by semicolons.

delegate void ModifyInt(int input);

ModifyInt addOneAndTellMe = x =>

{

int result = x + 1; Console.WriteLine(result);

};

Note that the statements are enclosed in braces {}.

 

GoalKicker.com – C# Notes for Professionals

478

Remember that statement lambdas cannot be used to create expression trees.

Section 84.6: Lambdas for both `Func` and `Action`

Typically lambdas are used for defining simple functions (generally in the context of a linq expression):

var incremented = myEnumerable.Select(x => x + 1);

Here the return is implicit.

However, it is also possible to pass actions as lambdas:

myObservable.Do(x => Console.WriteLine(x));

Section 84.7: Using lambda syntax to create a closure

See remarks for discussion of closures. Suppose we have an interface:

public interface IMachine<TState, TInput>

{

TState State { get; }

public void Input(TInput input);

}

and then the following is executed:

IMachine<int, int> machine = ...; Func<int, int> machineClosure = i => {

machine.Input(i); return machine.State;

};

Now machineClosure refers to a function from int to int, which behind the scenes uses the IMachine instance which machine refers to in order to carry out the computation. Even if the reference machine goes out of scope, as long as the machineClosure object is maintained, the original IMachine instance will be retained as part of a 'closure', automatically defined by the compiler.

Warning: this can mean that the same function call returns di erent values at di erent times (e.g. In this example if the machine keeps a sum of its inputs). In lots of cases, this may be unexpected and is to be avoided for any code in a functional style - accidental and unexpected closures can be a source of bugs.

Section 84.8: Passing a Lambda Expression as a Parameter to a Method

List<int> l2 = l1.FindAll(x => x > 6);

Here x => x > 6 is a lambda expression acting as a predicate that makes sure that only elements above 6 are returned.

Section 84.9: Basic lambda expressions

Func<int, int> add1 = i => i + 1;

Func<int, int, int> add = (i, j) => i + j;

GoalKicker.com – C# Notes for Professionals

479

// Behaviourally equivalent to:

int Add1(int i)

{

return i + 1;

}

int Add(int i, int j)

{

return i + j;

}

...

Console.WriteLine(add1(42)); //43

Console.WriteLine(Add1(42)); //43

Console.WriteLine(add(100, 250)); //350

Console.WriteLine(Add(100, 250)); //350

Section 84.10: Basic lambda expressions with LINQ

// assume source is {0, 1, 2, ..., 10}

var evens = source.Where(n => n%2 == 0);

// evens = {0, 2, 4, ... 10}

var strings = source.Select(n => n.ToString());

// strings = {"0", "1", ..., "10"}

Section 84.11: Lambda syntax with statement block body

Func<int, string> doubleThenAddElevenThenQuote = i => { var doubled = 2 * i;

var addedEleven = 11 + doubled; return $"'{addedEleven}'";

};

Section 84.12: Lambda expressions with

System.Linq.Expressions

Expression<Func<int, bool>> checkEvenExpression = i => i%2 == 0;

// lambda expression is automatically converted to an Expression<Func<int, bool>>

GoalKicker.com – C# Notes for Professionals

480