Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Ниже приведен результат выполнения этой программы. Обратите особое внимание на суммарное значение.0123Сумма 3 равна 6012345Сумма 5 равна 21
Как видите, подсчет по-прежнему выполняется как обычно. Но обратите вниманиена то, что сумма 5 теперь равна 21, а не 15! Дело в том, что переменная sum захватывается объектом ctObj при его создании в методе Counter(). Это означает, что онапродолжает существовать вплоть до уничтожения делегата count при "сборке мусора" в самом конце программы. Следовательно, ее значение не уничтожается после возврата из метода Counter() или при каждом вызове анонимного метода, когда происходит обращение к делегату count в методе Main().
Несмотря на то что применение захваченных переменных может привести к довольно неожиданным результатам, как в приведенном выше примере, оно все желогически обоснованно. Ведь когда анонимный метод захватывает переменную, онапродолжает существовать до тех пор, пока используется захватывающий ее делегат.В противном случае захваченная переменная оказалась бы неопределенной, когда онамогла бы потребоваться делегату.Лямбда-выражения
Несмотря на всю ценность анонимных методов, им на смену пришел более совершенный подход: лямбда-выражение. Не будет преувеличением сказать, что лямбда-выражение относится к одним из самых важных нововведений в С#, начиная с выпускаисходной версии 1.0 этого языка программирования. Лямбда-выражение основываетсяна совершенно новом синтаксическом элементе и служит более эффективной альтернативой анонимному методу. И хотя лямбда-выражения находят применение главнымобразом в работе с LINQ (подробнее об этом — в главе 19), они часто используютсяи вместе с делегатами и событиями. Именно об этом применении лямбда-выраженийи пойдет речь в данном разделе.
Лямбда-выражение — это другой собой создания анонимной функции. (Первыйее способ, анонимный метод, был рассмотрен в предыдущем разделе.) Следовательно,лямбда-выражение может быть присвоено делегату. А поскольку лямбда-выражениесчитается более эффективным, чем эквивалентный ему анонимный метод то в большинстве случаев рекомендуется отдавать предпочтение именно ему.Лямбда-оператор
Во всех лямбда-выражениях применяется новый лямбда-оператор =>, который разделяет лямбда-выражение на две части. В левой его части указывается входной параметр (или несколько параметров), а в правой части — тело лямбда-выражения. Оператор => иногда описывается такими словами, как "переходит" или "становится".
В C# поддерживаются две разновидности лямбда-выражений в зависимости от теласамого лямбда-выражения. Так, если тело лямбда-выражения состоит из одного выражения, то образуется одиночное лямбда-выражение. В этом случае тело выражения незаключается в фигурные скобки. Если же тело лямбда-выражения состоит из блокаоператоров, заключенных в фигурные скобки, то образуется блочное лямбда-выражение.При этом блочное лямбда-выражение может содержать целый ряд операторов, в томчисле циклы, вызовы методов и условные операторы if. Обе разновидности лямбда-выражений рассматриваются далее по отдельности.Одиночные лямбда-выражения
В одиночном лямбда-выражении часть, находящаяся справа от оператора =>, воздействует на параметр (или ряд параметров), указываемый слева. Возвращаемымрезультатом вычисления такого выражения является результат выполнения лямбда-оператора.
Ниже приведена общая форма одиночного лямбда-выражения, принимающегоединственный параметр.параметр => выражение
Если же требуется указать несколько параметров, то используется следующая форма.(список_параметров) => выражение
Таким образом, когда требуется указать два параметра или более, их следует заключить в скобки. Если же выражение не требует параметров, то следует использоватьпустые скобки.
Ниже приведен простой пример одиночного лямбда-выражения.count- => count + 2
В этом выражении count служит параметром, на который воздействует выражение count + 2. В итоге значение параметра count увеличивается на 2. А вот еще одинпример одиночного лямбда-выражения.n => n % 2 == 0
В данном случае выражение возвращает логическое значение true, если числовоезначение параметра n оказывается четным, а иначе — логическое значение false.
Лямбда-выражение применяется в два этапа. Сначала объявляется тип делегата, совместимый с лямбда-выражением, а затем экземпляр делегата, которому присваивается лямбда-выражение. После этого лямбда-выражение вычисляется при обращении кэкземпляру делегата. Результатом его вычисления становится возвращаемое значение.
В приведенном ниже примере программы демонстрируется применение двух одиночных лямбда-выражений. Сначала в этой программе объявляются два типа делегатов. Первый из них, Incr, принимает аргумент типа int и возвращает результат тогоже типа. Второй делегат, IsEven, также принимает аргумент типа int, но возвращаетрезультат типа bool. Затем экземплярам этих делегатов присваиваются одиночныелямбда-выражения. И наконец, лямбда-выражения вычисляются с помощью соответствующих экземпляров делегатов.// Применить два одиночных лямбда-выражения.using System;// Объявить делегат, принимающий аргумент типа int и// возвращающий результат типа int.delegate int Incr(int v);// Объявить делегат, принимающий аргумент типа int и// возвращающий результат типа bool.delegate bool IsEven(int v);class SimpleLambdaDemo { static void Main() { // Создать делегат Incr, ссылающийся на лямбда-выражение. // увеличивающее свой параметр на 2. Incr incr = count => count + 2; // А теперь использовать лямбда-выражение incr. Console.WriteLine("Использование лямбда-выражения incr: "); int x = -10; while(x <= 0) { Console.Write(x + " "); x = incr(x); // увеличить значение x на 2 } Console.WriteLine ("n"); // Создать экземпляр делегата IsEven, ссылающийся на лямбда-выражение, // возвращающее логическое значение true, если его параметр имеет четное // значение, а иначе — логическое значение false. IsEven isEven = n => n % 2 == 0; // А теперь использовать лямбда-выражение isEven. Console.WriteLine("Использование лямбда-выражения isEven: "); for(int i=l; i <= 10; i++) if(isEven(i)) Console.WriteLine(i + " четное."); }}
Вот к какому результату приводит выполнение этой программы.Использование лямбда-выражения incr:-10 -8 -6 -4 -2 0Использование лямбда-выражения isEven:2 четное.4 четное.6 четное.8 четное.10 четное.
Обратите в данной программе особое внимание на следующие строки объявлений.Incr incr = count => count + 2;IsEven isEven = n => n % 2 == 0;
В первой строке объявления экземпляру делегата incr присваивается одиночноелямбда-выражение, возвращающее результат увеличения на 2 значения параметраcount. Это выражение может быть присвоено делегату Incr, поскольку оно совместимо с объявлением данного делегата. Аргумент, указываемый при обращении к экземпляру делегата incr, передается параметру count, который и возвращает результатвычисления лямбда-выражения. Во второй строке объявления делегату isEven присваивается выражение, возвращающее логическое значение true, если передаваемыйему аргумент оказывается четным, а иначе — логическое значение false. Следовательно, это лямбда-выражение совместимо с объявлением делегата IsEven.
В связи со всем изложенным выше возникает резонный вопрос: каким образом компилятору становится известно о типе данных, используемых в лямбда-выражении, например, о типе int параметра count в лямбда-выражении, присваиваемом экземпляру делегата incr? Ответить на этот вопрос можно так: компилятор делает заключение о типе параметра и типе результата вычисления выраженияпо типу делегата. Следовательно, параметры и возвращаемое значение лямбда-выражения должны быть совместимы по типу с параметрами и возвращаемым значением делегата.
Несмотря на всю полезность логического заключения о типе данных, в некоторыхслучаях приходится явно указывать тип параметра лямбда-выражения. Для этого достаточно ввести конкретное название типа данных. В качестве примера ниже приведендругой способ объявления экземпляра делегата incr.Incr incr = (int count) => count + 2;