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

Этот тип соответствует делегатам с двумя входными параметрами произвольных типов (T, U). Тип возвращаемого значения определяется параметром шаблона TResult. Развивая далее эту концепцию, разработчики (Anders Hejlsberg и Don Box) ввели определение делегатных типов Func<> с тремя и четырьмя параметрами. Во всех случаях, последний параметр определяет тип возвращаемого значения. Эти типы покрывают огромное множество вариантов их использования. Если их не хватит, вы всегда можете создать новый делегатный тип Func<> (с пятью, шестью, и т. д. параметрами). Например:

public delegate TResult Func<T, U, V, W, X, TResult>(T t, U u, V v, W w, X x);

static double StupidFunc(int t, int u, int v, int w, string x)

{

return t + u + v + w + double.Parse(x);

}

Добавьте в Main код, который обратится к статическому методу StupidFunc с помощью generic-делегата:

Func<int, int, int, int, string, double> pf = StupidFunc;

Console.WriteLine("pf(1, 2, 3, 4, \"10\") = " + pf(1, 2, 3, 4, "10"));

Повторим общее правило. Объявление delegate res Func<a, b, c, d, e, res>(...); создает новый делегатный тип (generic delegate), который описывает любые функции, имеющие 5 входных параметров (a, b, c, d, e) и возвращающие значение типа res (не void — для void-методов используйте шаблон Action<>).

Итак, мы создали новый шаблон делегатных типов. После этого следует определить множество параметров шаблона, которое дает заказ компилятору на создание нового делегатного типа. Так, выбрав множество <int, int, int, int, string, double>, мы заставляем компилятор сгенерировать делегатный тип, настроенный на указанную сигнатуру. Другое множество типов заставит компилятор создать другой делегатный тип семейства Func<>.

Делегат pf делегатного типа Func<int, int, int, int, string, double> способен вызывать функции с указанной сигнатурой. Так как сигнатура статического метода StupidFunc соответствует делегатному типу, то делегат способен воспринять адрес функции StupidFunc в качестве своего задания (task). Вы помните, что каждый делегат имеет список заданий (InvocationList). Присвоение pf = StupidFunc помещает адрес функции StupidFunc в список заданий делегата.

Было сказано много слов, но суть проста. Тем не менее, надо учиться проговаривать определения, иначе полная картина делегатной модели останется в тумане или быстро забудется.

Лямбда-выражения

В .Net Framework 3.0 появилась новая синтаксическая конструкция, которая поддержана операцией =>. Операцию => (lambda operator) следует читать: трансформируется в. С помощью этой операции создаются λ-выражения (Lambda Expressions). Этот термин используется в функциональном программировании.

Напомню, что русскому термину операция соответствует английский — operator, а русскому термину оператор соответствует английский — statement. Термин lambda operator следует переводить, как лямбда-операция. Подобные курьезы называют ложными друзьями переводчика.

В математике операции => соответстует прием, называемый подстановка выражения. Cмысл операции в языке C# легче понять, если рассмотреть эквивалентные ей, но уже знакомые конструкции языка. Приведем пример, иллюстрирующий шаги развития концепции λ-выражений. Далее на каждом шаге выполняется один и тот же код, но способ его запуска слегка изменяется.

¨  На первом шаге рассмотрим элементарный код.

static float Half (int x) { return x/2.0f; }

static void Main()

{

Console.WriteLine (Half (3));  // 1,5

Console.WriteLine (Half (-3)); // –1,5

}

Пока мы не видим ничего нового, такой подход к вычислению часто повторяющихся выражений хорошо известен. В приведенном варианте он весьма неэффективен, но смысл его понятен — всякий раз, когда надо найти половину целого числа, вызывается внешняя функция (в C# — это метод класса). Реализация неэффективна, так как в метод надо передать параметр (через стек), а затем еще вернуть результат.