Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык C# и основы платформы .NET.docx
Скачиваний:
57
Добавлен:
11.05.2015
Размер:
203.66 Кб
Скачать

21. Анонимные методы и лямбда-выражения

Назначение анонимных методов(anonymous methods) заключается в том, чтобы сократить объём кода, который должен писать разработчик при использовании делегатов. Если рассмотреть примеры предыдущего параграфа, то очевидно, что даже для объектов-делегатов, содержащих минимум действий, необходимо создавать метод (а, возможно, и отдельный класс) и инкапсулировать этот метод в делегате. При применении анонимных методов формируется безымянный блок кода, который назначается объекту-делегату.

Синтаксис объявления анонимного метода включает ключевое слово delegateи список формальных параметров. Ковариантность и контравариантность делегатов работает и в случае применения анонимных методов. Дополнительным правилом является возможность описать анонимный метод без параметров, если параметры не используются в теле метода, а делегат не имеетout-параметров.

Модифицируем фрагмент кода из предыдущего параграфа, используя анонимные методы:

int[] a = {1, 2, 3};

Transformer<int,int> t =delegate(intx) {returnx * x; };

a.Transform(t);

Лямбда-выраженияилямбда-операторы– это альтернативный синтаксис записи анонимных методов. Начнём с рассмотрения лямбда-операторов. Пусть имеется анонимный метод:

Func<int, bool> f = delegate(int x)

{

inty = x - 100;

returny > 0;

};

При использовании лямбда-операторов список параметров отделяется от тела оператора символами =>, а ключевое словоdelegateне указывается:

Func<int,bool> f = (intx) => {inty = x - 100;returny > 0; };

Более того, так как мы уже фактически указали тип аргумента лямбда-оператора слева при объявлении f, то его можно не указывать справа. В случае если у нас один аргумент, можно опустить обрамляющие его скобки1:

Func<int,bool> f = x => {inty = x - 100;returny > 0; };

Когда лямбда-оператор содержит в своём теле единственный оператор return, он может быть записан в компактной формелямбда-выражения:

Func<int,bool> f_2 = x => x > 0;

Заметим, что для переменной, в которую помещается лямбда-оператор или лямбда-выражение, требуется явно указывать тип – varиспользовать нельзя.

Рассмотрим пример, демонстрирующий применение лямбда-выражений. Используем метод Transform()из предыдущего параграфа, чтобы получить из числового массива массив булевых значений, а из него – массив строк. Обратите внимание на компактность получаемого кода.

int[] a = {1, 2, 3};

string[] s = a.Transform(x => x > 2).Transform(y => y.ToString());

// s содержит "False", "False", "True"

Анонимные методы и лямбда-операторы способны захватывать внешний контекст вычисления. Если при описании тела анонимного метода применялась внешняя переменная, вызов метода будет использовать текущеезначение переменной. Захват внешнего контекста иначе называютзамыканием(closure).

int[] a = {1, 2, 3};

int external = 0; // внешняя переменная

Transformer<int,int> t = x => x + external;// замыкание

external = 10; // изменили переменную после описания t

int[] b = a.Transform(t); // прибавляет 10 к каждому элементу

Эффектный приём использования замыканий – функции, реализующие мемоизацию. Мемоизация(memoization)– это кэширование результатов вычислений. В следующем примере описан метод расширения для строк, который возвращает функцию подсчёта встречаемости символа в строке.

publicstaticFunc<char,int> FrequencyFunc(thisstringtext)

{

int[] freq =newint[char.MaxValue];

foreach(charcintext)

{

freq[c]++;

}

returnch => freq[ch];

}

// использование частотной функции

varf ="There is no spoon".FrequencyFunc();

Console.WriteLine(f('o'));