C# 4.0 полное руководство - 2011 - Герберт Шилдт
Шрифт:
Интервал:
Закладка:
Ниже приведен результат выполнения этой программы. Обратите особое внимание на суммарное значение.
0
1
2
3
Сумма 3 равна 6
0
1
2
3
4
5
Сумма 5 равна 21
Как видите, подсчет по-прежнему выполняется как обычно. Но обратите внимание на то, что сумма 5 теперь равна 21, а не 15! Дело в том, что переменная sum захватывается объектом ctOb j при его создании в методе Counter (). Это означает, что она продолжает существовать вплоть до уничтожения делегата count при "сборке мусо-ра" в самом конце программы. Следовательно, ее значение не уничтожается после возврата из метода Counter () или при каждом вызове анонимного метода, когда происходит обращение к делегату count в методе Main ().
Несмотря на то что применение захваченных переменных может привести к довольно неожиданным результатам, как в приведенном выше примере, оно все же логически обоснованно. Ведь когда анонимный метод захватывает переменную, она продолжает существовать до тех пор, пока используется захватывающий ее делегат. В противном случае захваченная переменная оказалась бы неопределенной, когда она могла бы потребоваться делегату.
Лямбда-выражения
Несмотря на всю ценность анонимных методов, им на смену пришел более совершенный подход: лямбда-выражение. Не будет преувеличением сказать, что лямбда-выражение относится к одним из самых важных нововведений в С#, начиная с выпуска исходной версии 1.0 этого языка программирования. Лямбда-выражение основывается на совершенно новом синтаксическом элементе и служит более эффективной альтернативой анонимному методу. И хотя лямбда-выражения находят применение главным образом в работе с LINQ (подробнее об этом — в главе 19), они часто используются и вместе с делегатами и событиями. Именно об этом применении лямбда-выражений и пойдет речь в данном разделе.
Лямбда-выражение — это другой собой создания анонимной функции. (Первый ее способ, анонимный метод, был рассмотрен в предыдущем разделе.) Следовательно, лямбда-выражение может быть присвоено делегату. А поскольку лямбда-выражение считается более эффективным, чем эквивалентный ему анонимный метод, то в большинстве случаев рекомендуется отдавать предпочтение именно ему.
Лямбда-оператор
Во всех лямбда-выражениях применяется новый лямбда-оператор =>, который разделяет лямбда-выражение на две части. В левой его части указывается входной параметр (или несколько параметров), а в правой части — тело лямбда-выражения. Оператор => иногда описывается такими словами, как "переходит" или "становится".
В C# поддерживаются две разновидности лямбда-выражений в зависимости от тела самого лямбда-выражения. Так, если тело лямбда-выражения состоит из одного выражения, то образуется одиночное лямбда-выражение. В этом случае тело выражения не заключается в фигурные скобки. Если же тело лямбда-выражения состоит из блока операторов, заключенных в фигурные скобки, то образуется блочное лямбда-выражение. При этом блочное лямбда-выражение может содержать целый ряд операторов, в том числе циклы, вызовы методов и условные операторы if. Обе разновидности лямбда-выражений рассматриваются далее по отдельности.
Одиночные лямбда-выражения
В одиночном лямбда-выражении часть, находящаяся справа от оператора =>, воздействует на параметр (или ряд параметров), указываемый слева. Возвращаемым результатом вычисления такого выражения является результат выполнения лямбда-оператора.
Ниже приведена общая форма одиночного лямбда-выражения, принимающего единственный параметр.
параметр => выражение
Если же требуется указать несколько параметров, то используется следующая форма.
(список_параметров) => выражение
Таким образом, когда требуется указать два параметра или более, их следует заключить в скобки. Если же выражение не требует параметров, то следует использовать пустые скобки.
Ниже приведен простой пример одиночного лямбда-выражения.
count- => count + 2
В этом выражении count служит параметром, на который воздействует выражение count + 2. В итоге значение параметра count увеличивается на 2. А вот еще один пример одиночного лямбда-выражения.
n => п % 2 == О
В данн“ом случае выражение возвращает логическое значение true, если числовое значение параметра п оказывается четным, а иначе — логическое значение false.
Лямбда-выражение применяется в два этапа. Сначала объявляется тип делегата, совместимый с лямбда-выражением, а затем экземпляр делегата, которому присваивается лямбда-выражение. После этого лямбда-выражение вычисляется при обращении к экземпляру делегата. Результатом его вычисления становится возвращаемое значение.
В приведенном ниже примере программы демонстрируется применение двух одиночных лямбда-выражений. Сначала в этой программе объявляются два типа делегатов. Первый из них, Inc г, принимает аргумент типа int и возвращает результат того же типа. Второй делегат, IsEven, также принимает аргумент типа int, но возвращает результат типа bool. Затем экземплярам этих делегатов присваиваются одиночные лямбда-выражения. И наконец, лямбда-выражения вычисляются с помощью соответствующих экземпляров делегатов.
// Применить два одиночных лямбда-выражения.
11 Объявить делегат, принимающий аргумент типа 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? Ответить на этот вопрос можно так: компилятор делает заключение о типе параметра и типе результата вычисления выражения по типу делегата. Следовательно, параметры и возвращаемое значение лямбда-выражения должны быть совместимы по типу с параметрами и возвращаемым значением делегата.