День двести третий. #ЗаметкиНаПолях

Захват переменных лямбда-выражениями

Внутри лямбда-выражения вы можете использовать любую переменную, которую вы могли бы использовать в обычном коде в данном месте. Это может быть статическое поле, поле экземпляра (если вы пишете лямбда-выражение внутри экземплярного метода), переменная this, параметры метода или локальные переменные. Все они называются захваченными переменными, потому что они объявленны вне непосредственного контекста лямбда-выражения. Для сравнения параметры лямбда-выражения или локальные переменные, объявленные внутри лямбда-выражения не являются захваченными переменными. В следующем примере показан захват различных переменных лямбда-выражением:



class CapturedVariablesDemo

{

private string instanceField = "instance field";

public Action<string> CreateAction(string methodParameter)

{

string methodLocal = "method local";

string uncaptured = "uncaptured local";

Action<string> action = lambdaParameter =>

{

string lambdaLocal = "lambda local";

Console.WriteLine("Instance field: {0}", instanceField);

Console.WriteLine("Method parameter: {0}", methodParameter);

Console.WriteLine("Method local: {0}", methodLocal);

Console.WriteLine("Lambda parameter: {0}", lambdaParameter);

Console.WriteLine("Lambda local: {0}", lambdaLocal);

};

methodLocal = "modified method local";

return action;

}

}



var demo = new CapturedVariablesDemo();

Action<string> action = demo.CreateAction("method argument");

action("lambda argument");



Вывод:

Instance field: instance field

Method parameter: method argument

Method local: modified method local

Lambda parameter: lambda argument

Lambda local: lambda local



В этом примере задействованы следующие переменные:

- instanceField - поле экземпляра класса CapturedVariablesDemo,

захватывается лямбда-выражением.

- methodParameter - параметр метода CreateAction, захватывается лямбда-выражением.

- methodLocal - локальная переменная метода CreateAction, захватывается и изменяется лямбда-выражением.

- uncaptured - локальная переменная метода CreateAction, не используется лямбда-выражением, поэтому не захватывается им.

- lambdaParameter - параметр лямбда-выражения, не является захваченной переменной.

- lambdaLocal - локальная переменная в лямбда-выражении, не является захваченной переменной.



Важно понимать, что лямбда-выражение захватывает сами переменные, а не значения переменных в момент создания делегата. Если вы изменили какую-либо из захваченных переменных между временем создания делегата и его вызовом, лямбда-выражение «увидит» эти изменения. Аналогично, лямбда-выражение может изменить значение захваченных переменных. Более того, в этом случае каждый последующий вызов лямбда-выражения будет «видеть» изменения захваченных переменных, сделанные предыдущими вызовами.



Источник: Jon Skeet “C# In Depth”. 4th ed – Manning Publications Co, 2019. Глава 3.