Г. Шилдт - С#4.0 Полное руководство (1160795), страница 93
Текст из файла (страница 93)
Несмотря на то что применение захваченных переменных может привести к довольно неожиданным результатам, как в приведенном выше примере, оно все же логически обоснованно. Ведь когда анонимный метод захватывает переменную, она продолжает существовать до тех пор, пока используетсл захватывающий ее делегат. В противном случае захваченная переменная оказалась бы неопределенной, когда она могла бы потребоваться делегату.
Лямбда-выражения Несмотря на всю ценность анонимных методов, им на смену пришел более совершенный подход: ллмбда-выражение. Не будет преувеличением сказать, что лямбдавыражение относится к одним из самых важных нововведений в СФ, начиная с выпуска исходной версии 1.0 этого лзыка программирования. Ллмбда-выражение основывается на совершенно новом синтаксическом элементе и служит более эффективной альтернативой анонимному методу. И хотя ллмбда-выражения находят применение главным образом в работе с (.1)ЧО (подробнее об этом — в главе 19), они часто используютсл и вместе с делегатами и событиями.
Именно об этом применении ллмбда-выражений и пойдет речь в данном разделе. Лямбда-выражение — это другой собой создания анонимной функции. (Первый ее способ, анонимный метод был рассмотрен в предыдущем разделе.) Следовательно, лямбда-выражение может быть присвоено делегату.
А поскольку лямбда-выражение считается более эффективным, чем эквивалентный ему анонимный метод то в большинстве случаев рекомендуется отдавать предпочтение именно ему, дя)ыбда-оператор Во всех лямбда-выражениях применяется новый лямбда-оператор =>, который разделяет ллмбда-выражение на две части. В левой его части указываетсл входной параметр (или несколько параметров), а в правой части — тело лямбда-выражения. Оператор => иногда описывается такими словами, как "переходит" или "становится". Глава 1б. Делегаты, события и лямбда-выражения 489 В СФ поддерживаются две разновидности лямбда-выражений в зависимости от тела самого лямбда-выражения.
Так, если тело лямбда-выражения состоит из одного выражения, то образуется одиночное лямбда-выражение. В этом случае тело выражения не заключается в фигурные скобки. Если же тело лямбда-выражения состоит из блока операторов, заключенных в фигурные скобки, то образуется блочное лямбда-выражение. При этом блочное лямбда-выражение может содержать целый ряд операторов, в том числе циклы, вызовы методов и условные операторы 11. Обе разновидности лямбдавыражений рассматриваются далее по отдельности.
Одиночные йямбда-выражения В одиночном лямбда-выражении часть, находящаяся справа от оператора =>, воздействует на параметр (или ряд параметров), указываемый слева. Возвращаемым результатом вычисления такого выражения является результат выполнения лямбдаоператора. Ниже приведена общая форма одиночного лямбда-выражения, принимающего единственный параметр.
параметр => еыраиение Если же требуется указать несколько параметров, то используется следующая форма. (список параметров) => еырееение Таким образом, когда требуется указать два параметра или более, их следует заключить в скобки. Если же выражение не требует параметров, то следует использовать пустые скобки. Ниже приведен простой пример одиночного лямбда-выражения.
ссыпь =.> соипг т 2 В этом выражении соипс служит параметром, на который воздействует выражение соппс + 2. В итоге значение параметра ссппс увеличивается на 2. А вот еще один пример одиночного лямбда-выражения. п => и Ъ 2 == 0 В данном случае выражение возвращает ловическое значение сгпе, если числовое значение параметра и оказывается четным, а иначе — логическое значение га1 ее. Лямбда-выражение применяется в два этапа. Сначала обьявляется тип делегата, совместимый с лямбда-выражением, а затем экземпляр делегата, которому присваивается лямбда-выражение.
После этого лямбда-выражение вычисляется при обращении к экземпляру делегата. Результатом его вычисления становится возвращаемое значение. В приведенном ниже примере программы демонстрируется применение двух одиночных лямбда-выражений. Сначала в этой программе объявляются два типа делегатов. Первый из них, 1псг, принимает аргумент типа Тпс и возвращает результат того же типа. Второй делегат, 1 5Еуеп, также принимает аргумент типа Тпг, но возвращает результат типа Ьоо1. Затем экземплярам этих делегатов присваиваются одиночные лямбда-выражения. И наконец, лямбда-выражения вычисляются с помощью соответствующих экземпляров делегатов. Применить Пее одиночных яямвяа-выражения. изьпо 5узпевн 490 Часть ).
Язык С() // Объявить делегат, принимающий аргумент типа ьпг и // возвращающий результат типа ьпг. бе1едасе ьпс 1псг(1пс ч); // Объявить делегат, принимающий аргумент типа гпс и // возвращающий результат типа Ьоо1. бе1есаве Ьоо1 1экчеп(гпт ч); с1аэя 81щр1еЬащос(аОещо [ эсасъс чо10 Иаьп () ( Создать делегат 1псг, ссылающийся на лямбда-выражение, // увеличивающее свой параметр на 2.
1псг ьпсг = соппг => соппг т 21 // Я теперь испольэовать лямбда-выражение ьпсг. Сопяо1е.игьсеъьпе("Использование лямбда-выражения ъпсг: ") 1по х = -10) нь11е(х <= О) ( Сопво1е.нгьое(х + " "); х = ьпсг(х); // увеличить значение х на 2 Сопяо1е.аг1сеъ1пе("1п"); // Создать экземпляр делегата 1яЕчеп, ссылающийся на лямбда-выражение, // возвращающее логическое значение Сгое, если его параметр имеет четное // значение, а иначе — логическое значение та1ве. 1эЕчеп ьяЕчеп = и => и $ 2 == 0; // Я теперь использовать лямбда-выражение ЕэЕчеп. Сопяо1е.иг1се11пе("Использование ллмбда-выражения 1эЕчеп. ""); тот(1пт 1=1; 1 < 10; 1++) 11(ьяЕчеп(1)) Сопэо1е.иг1Геъьпе(1 + " четное."); Вот к какому результату приводит выполнение этой программы.
Использование лямбда-выражения ьпсг: — 10 -8 — б — 4 -2 0 Использование лямбда-выражения тэЕчеп: 2 четное. 4 четное. б четное. 8 четное. 10 четное. Обратите в данной программе особое внимание на следующие строкй объявлений. 1псг ьпсг = соппс => соопс + 2) 1яЕчеп ьяЕчеп = и => и $ 2 == 0; Глава 15.
Делегаты, события и вяыбда-выражвиия 491 В первой строке объявления экземпляру делегата 1псг присваивается одиночное лямбда-выражение, возвращающее результат увеличения на 2 значения параметра оопп С. Это выражение может быть присвоено делегату 1псг, поскольку оно совместимо с объявлением данного делегата.
Аргумент, указываемый при обращении к экземпляру делегата ьпсг, передаетсл параметру соппс, который и возвращает результат вычисления лямбда-выраженил. Во второй строке объявления делегату 1звчеп присваивается выражение, возвращающее логическое значение Сгпе, если передаваемый ему аргумент оказывается четным, а иначе — логическое значение Га1зе. Следовательно, это лямбда-выражение совместимо с объявлением делегата 1зКчеп. В связи со всем изложенным выше возникает резонный вопрос каким образом компилятору становится известно о типе данных, используемых в лямбдавыражении, например, о типе Тпг параметра соппс в лямбда-выражении, присваиваемом экземпляру делегата ьпсг? Ответить на этот вопрос можно так: компиллтор делает заключение о типе параметра и типе результата вычисления выражения по типу делегата.
Следовательно, параметры и возвращаемое значение ллмбдавыражения должны быть совместимы по типу с параметрами и возвращаемым значением делегата. Несмотря на всю полезность логического заключения о типе данных, в некоторых случаях приходится явно указывать тип параметра ллмбда-выражения. Для этого достаточно ввести конкретное название типа данных.
В качестве примера ниже приведен другой способ объявления экземпляра делегата гпсг. 1псг гпсг = (гпс сорос) => ссдпс а 2; Как видите, соппс теперь явно объявлен как параметр типа г пг. Обратите также внимание на использование скобок. Теперь они необходимы. (Скобки могут быть опущены только в том случае, если задается лишь один параметр, а его тип явно не указывается.) В предыдущем примере в обоих ллмбда-выражениях использовался единственный параметр, но в целом у лямбда-выражений может быть любое количеспю параметров, в том числе и нулевое. Если в лямбда-выражении используется несколько параметров, их необходимо заключить в скобки. Ниже приведен пример использования лямбдавыражения с целью определить, находится ли значение в заданных пределах. (1он, Ьгдп, ча1) => ча1 >= 1он ЬЬ ча1 <= Ьгчп; А вот как объявляется тип делегата, совместимого с этим лямбда-выражением.
г(е1ечасе Ьоо1 1пкапде(1пг 1онег, гпг пррег, гпс ч)1 Следовательно, экземпляр делегата 1пйапЧе может быть создан следующим образом. 1пяапде гапдеОК = (1он, Ь1ЧЬ, ча1) => ча1 >= 1он ЬЬ ча1 <= Ь1ЧЬ; После этою одиночное лямбда-выражение может быль выполнено так, как показано ниже. 11(гапчеОК(1, 5, 3)) Сопао1е.нггсеъгпе( "Число 3 находится в пределах от 1 до 5.") И последнее замечание: внешние переменные могут использоваться и захватываться в лямбда-выражениях таким же образом, как и в анонимных методах.
492 Часть!. Язык С№ Блочные ляыбда-выражения Как упоминалось выше, существуют две разновидности лямбда-выражений. Первая из них, одиночное лямбда-выражение, была рассмотрена в предыдущем разделе. Тело такою лямбда-выражения состоит только из одного выражения. Второй разновидностью является блочное лямбг3а-вырахгение. Для такого лямбда-выражения характерны расширенные возможности выполнения различных операций, поскольку в его теле допускается указывать несколько операторов. Например, в блочном лямбда-выражении можно использовать циклы и условные операторы 15, объявлять переменные и т.д. Создать блочное лямбда-выражение нетрудно.
Для этого достаточно заключить тело выражения в фигурные скобки. Помимо возможности использовать несколько операторов, в остальном блочное лямбда-выражение, практически ничем не отличается от только что рассмотренного одиночного лямбда-выражения. Ниже приведен пример использования блочного лямбда-выражения для вычисления и возврата факториала целого значения. Продемонстрировать применение блочного лямбда-выражения. озьпд Яузгеш; Делегат 1пгбр принимает один аргумент типа ьпС и возврашает результат типа тпс.