Вспоминаю, что в языках 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++). Но они иллюстрируют синтаксические правила создания и использования λ-выражений.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.