Изучение лямбда-выражений в C# - Создание выражения из другого выражения
ОГЛАВЛЕНИЕ
Создание выражения из другого выражения
Можно взять дерево выражения и изменить его, чтобы создать другое выражение из него. В следующем примере начинаем с лямбда-выражения x *x, затем меняем это выражение, прибавляя к нему 2. Рассмотрим пример:
public static void CreatingAnExpressionFromAnotherExpression()
{
Expression<Func<int, int>> square = x => x * x;
BinaryExpression squareplus2 = Expression.Add(square.Body,
Expression.Constant(2));
Expression<Func<int, int>> expr = Expression.Lambda<Func<int, int>>(squareplus2,
square.Parameters);
Func<int, int> compile = expr.Compile();
Console.WriteLine(compile(10));
}
Начинаем с лямбда-выражения, возвращающего square(квадрат):
Expression<Func<int, int>> square = x => x * x;
Затем генерируем тело нового лямбда-выражения, используя тело первого лямбда-выражения и прибавляя константу 2 к нему и назначая ее двоичному выражению:
BinaryExpression squareplus2 = Expression.Add(square.Body, Expression.Constant(2));
На последнем шаге генерируется новое лямбда-выражение путем соединения тела с параметрами из первого лямбда-выражения. В операторе ниже ссылка параметра должна точно совпадать со ссылкой из первого лямбда-выражения, являющегося square.Parameters. Нельзя создать новый экземпляр коллекции параметров, что дает ошибку при выполнении.
Expression<Func<int, int>> expr = Expression.Lambda<Func<int, int>>(squareplus2,
square.Parameters);
Замыкания и лямбда-выражения
Замыкание – концепция, взятая из функционального программирования. Оно захватывает или использует переменную, находящуюся вне области видимости лямбда-выражения. Это значит, что вы можете использовать внутри лямбда-выражения переменные, объявленные вне области видимости лямбда-выражения — вы можете использовать и захватить переменную, находящуюся вне рамок лямбда-выражения. Это имеет свои плюсы, но может вызвать проблемы, так как внешний контекст может менять значение переменной. Разберем пример лямбда-выражения с учетом замыкания.
public static void LambdaWithClosure()
{
int mulitplyby = 2;
Func<int, int> operation = x => x * mulitplyby;
Console.WriteLine(operation(2));
}
В примере выше используется переменная mulitplyby внутри лямбда-выражения, хотя она объявлена вне области видимости выражения. Такой принцип называется захватом переменной. На заднем плане компилятор C# берет все захваченные переменные и помещает их в сгенерированный класс. При использовании лямбда-выражений с внешними переменными, сборщик мусора не собирает их, и они существуют, пока не используются лямбда-выражениями и выражение не покинет область видимости.
Есть определенные ограничения при использовании лямбда-выражений с параметром с ключевым словом ref и out. Если переменная передается с ключевым словом ref или out, надо явно задать тип параметра, потому что компилятор не может вывести тип переменной. Как показано в примере ниже:
delegate void OutParameter(out int i);
delegate void RefParameter(ref int i);
public static void GotchasWithLambdas()
{
//пример с параметром out int i;
OutParameter something = (out int x) => x = 5;
something(out i);
Console.WriteLine(i);
//пример с параметром ref.
int a = 2;
RefParameter test = (ref int x) => x++;
test(ref a);
Console.WriteLine(a);
}
Обратите внимание, что в коде выше явно задан тип параметра int в обоих случаях, ref и out. Если опустить тип параметра, компилятор выдаст ошибку.
Другое ограничение при использовании лямбд заключается в том, что нельзя использовать ключевое слово params в типе параметра для лямбда-выражения, независимо от того, задан явно тип параметра или нет. Следующий код не компилируется, потому что определение параметра описано ключевым словом params:
delegate void ParmsParameter(params int[] ints);
public static void LambdaWithParam()
{
ParmsParameter par = (params int[] ints) =>
{
foreach (int i in ints)
{
Console.WriteLine(i);
}
};
}
Вывод
В данной статье был рассмотрен синтаксис лямбда-выражения — как оно заменяет безымянный метод. Также сказано, что лямбда-выражения отличаются от безымянных методов из-за выведения типов и их способности легко превращаться в делегаты или деревья выражений. Были изучены ограничения параметров лямбда-выражений и то, как писать выражение с нуля и компилировать его в делегат, и наоборот.