Секреты LINQ. Автогенерируемые свойства. Инициализаторы объектов и коллекций, страница 7

Вспоминаю, что в языках PL/I или Pascal для повышения эффективности в таких случаях пользовались вложенными функциями. Они заметно дешевле, так как видят все окружающие их переменные и не требуют передачи параметров. Нечто подобное теперь появилось и в C#.

¨  На втором шаге развития концепции (в C# 2.0) добавили подход на основе анонимного метода (делегата). Он призван упростить рассмотренные манипуляции.

delegate float FuncType (int x);     // Заявлен делегатный тип

static void Main()

{

FuncType Half = delegate(int x) { return x / 2.0f; }; // Анонимный метод делегатного типа

Console.WriteLine (Half (3));

}

Этот код выполняет те же действия, что и предыдущий, вполне традиционный вариант. Как видите, после описателя delegate отсутствует имя метода (поэтому делегат и называется анонимным). Кроме того, тело внешней функции перекочевало внутрь метода Main. Такое решение более экономно.

Учитывая сказанное ранее о generic-делегатах (см. описание Func<>), перепишем наш фрагмент в более простом виде.

static void Main()

{

Func<int, float> Half = delegate(int x) { return x / 2.0f; };

Console.WriteLine (Half (3));

}

Определение делегатного типа FuncType отсутствует, его заменило описание Func<>, которое уже имеется в пространстве имен System. Для решения задачи достаточно лишь настроить шаблон Func на нужные типы данных (int, float).

¨  Следующий шаг развития концепции привел к созданию Lambda Expressions — подстановочных выражений. Теперь вместо:

delegate(int x) { return x / 2.0f; };

используют подстановочное выражение:

x => x / 2.0f; // λ-выражение

Заголовок и часть тела метода растворились. Мы не видим ни фигурных скобок, ни оператора return. Параметр x остался в урезанном виде. Применим этот трюк и наш фрагмент станет еще короче.

static void Main()

{

Func<int, float> Half = x => x / 2.0f;  // λ-выражение

Console.WriteLine (Half(3));

}

Эта версия кода дает тот же результат, что и предыдущие две.

Лямбда-выражения похожи на стенографию. Стенографист — умершая профессия, которая некогда была достаточно высоко оплачиваема. Стенографист с помощью особых правил и приемов успевал руками записать беглую речь оратора (почти дословно). Выражение x => x / 2.0f — это краткая запись более длинной версии: delegate(int x) { return x / 2.0f; };

Допустимы некоторые промежуточные, гибридные варианты записи λ-выражений. Например:

delegate float MyFunc (int x);  // Заявлен делегатный тип

static void Main()

{

MyFunc Half = x => x / 2.0f; // λ-выражение

Console.WriteLine(Half(3));

}

Само λ-выражение также допускает разные формы записи. Например, все три приведенные ниже формы эквивалентны.

1.        x => x / 2.0f;

2.        (int x) => x / 2.0f;

3.        (int x) => { return x / 2.0f; };

Проанализируйте следующие два эквивалентных фрагмента кода, которые иллюстрируют использование типов Action и Func, и вычислите в уме, что они выведут в консольное окно.

int n = 0;

Func<int> Go = () => n++;

Console.WriteLine(Go() + ", " + Go() + ", " + Go() + ", " + Go());

__________________________________________________________________

n = 0;

Action Do = () => Console.Write(n+++", ");

Do(); Do(); Do(); Do();

Пояснения

Делегатный тип Action позволяет пользоваться функциями, не возвращающими значений в точку вызова. В примере делегату типа Action присвоено имя Do. Метод Do, реализованный в виде λ-выражения, производит вывод переменной n, увеличивает ее значение и дополняет вывод запятой с пробелом.

Если при мысленном анализе выражений возникнут трудности, то заставьте студию выполнить этот код и обдумайте результат. Просмотрите справку по шаблонам Func и Action. Приведенные примеры описывают абсурдные ситуации (вместо всей этой суеты можно было просто несколько раз вывести n++). Но они иллюстрируют синтаксические правила создания и использования λ-выражений.