Захват переменных анонимными методами. Цепочка вызовов.
При объявлении анонимных методов любые переменные, объявленные вне контекста анонимного метода, но доступные внутри него, включая ссылку this, рассматриваются как внешние переменные. И если тело анонимного метода ссылается на эти переменные, то говорят, что анонимный метод "захватил" переменную.
Public static void Search(list <string> list, string substr)
{
return list.Find(delegate(string x) {return x.Starts with (substr);});
}
Здесь в метод Search передается параметр substr. Этот параметр используется в анонимном методе (вместо него может использовать другая любая локальная переменная, но интересны те локальные переменные, которые хранятся в стеке). Далее из анонимного метода создается делегат, но он может быть передан в другие точки программы, т.е. возможна ситуация, когда делегат вызывается после того, как локальная переменная вышла из зоны видимости.
Локальные переменные или параметры, использованные в теле анонимного метода подчиняются другим правилам жизненного цикла и они говорят, что анонимный метод захватил переменную.
Для захвата переменных, которые являются типами значений, создаются анонимные объекты соответствующих типов, в которых с помощью операций установки присваиваются нужные значения, т.е. анонимный метод работает не с самой переменной, а с ее копией расположенной в куче.
19. Лямбда-выражения и лямбда-операторы. Синтаксис, назначение и простейшие формы.
Используя λ-выражения, можно кратко определять функциональные объекты для использования в любое время. C# всегда поддерживал эту возможность через делегаты, посредством которых создается функциональный объект (в форме делегата) и к нему привязывается код обратного вызова во время создания. λ-выражения связывают эти два действия – создание и подключение – в один выразительный оператор кода. Вдобавок можно легко ассоциировать среду с функциональными объектами.
λ-выражение – это синтаксис, посредством которого можно объявлять анонимные функции (делегаты) более гладким и выразительным способом.
λ-выражение – особая форма функционального программирования для императивного языка.
λ-выражение позволяет в краткой форме определять функциональные объекты.
В основе λ-выражений лежат делегаты. В обычной форме нужно создать делегат и подключить его к методу для обратного вызова. λ-выражение позволяет соединить два этапа в один. У λ-выражений имеется две формы:
1) пишется некий код, который завершается оператором return. Форма, которая наиболее прямо заменяет анонимные методы, представляет собой блок кода, заключенный в фигурные скобки. Это λ-операторы. Такие λ-операторы – прямая замена анонимных методов.
2) λ-выражения предоставляют еще более сокращенный способ объявлять анонимный метод и не требуют ни кода в фигурных скобках, ни оператора return.
Оба типа λ-выражений могут быть преобразованы в делегаты. Однако λ-выражения без блоков операторов можно преобразовать в деревья выражений с помощью типов из пространства имен System.Linq.Expressions. Другими словами, функция, описанная в коде, превращается в данные.
Синтаксис:
<(список операторов)> => λ-выражение
Например, следующее λ-выражение может быть использовано в качестве делегата, принимающего один параметр и возвращающего результат выполнения операции над параметром: х => х/2, где х - атрибут, а х/2 - λ-выражение. Это говорит следующее: “взять x в качестве параметра и вернуть результат следующей операции в x”.
λ-выражение лишено информации о типе. Это не значит, что выражение не имеет типа. Вместо этого компилятор выводит тип аргумента и тип результата из контекста его использования, и отсюда следует, что если вы присваиваете λ-выражение делегату, типы определения делегата используются для определения типов внутри λ-выражения.
Func<int, double> expr = x => x/2;
Console.WriteLine (expr(5));
Тип Func<> – это новый вспомогательный тип, представленный в пространстве имен System, который используется для объявления простых делегатов, принимающих до четырех аргументов и возвращающих результат.
В данном случае объявляется переменная expr, которая является делегатом, принимающим int и возвращающим double.
Public delegate TResult Func <TSouree; TResult>(TSouree souree);
Func<int, int, int> exp r = (x, y) => x+y;
λ-выражения как и делегаты в состоянии захватывать внешние переменные.
Например, необходимо из списка выбрать элементы удовлетворяющие некоторому условию и высчитать их количество.
public static int GetCount<TItem>(IList <TItem>souree, Predicate<TItem> predicate)
{
int count = 0;
foreach(var x in souree)
if(predicate(x)) count ++;
return count;
}
List <string> list = new List<string>() {};
int c = GetCount(list, x => x.Substr(“Петуш”) >= 0);
Или
List <string> list = new List<int>() {2, 3, 4, 7}
int c = GetCount(list, x => x %2 == 0);
Кроме λ-выражений есть λ-операторы. Отличие состоит в том, что λ-операторы имеют внутри себя оператор return аналогично анонимным методам.
Func <int, int, int> expr = (x, y) =>{return x+y;}
И есть одно главное отличие между λ-выражениями с блоками операторов и простыми λ-выражениями. Первые могут быть преобразованы только в типы делегатов, в то время как вторые – и в делегаты, и в деревья выражений, посредством семейства типов, сосредоточенных вокруг System.Linq.Expressions.Expression<T>.