Шрифт:
Начиная с версии 3.0, в C# предусмотрены две разновидности анонимных функ ций: анонимные методы и лямбда-выражения. Анонимные методы были внедрены в C# еще в версии 2.0, а лямбда-выражения — в версии 3.0. В целом лямбда-выражение со вершенствует принцип действия анонимного метода и в настоящее время считается более предпочтительным для создания анонимной функции. Но анонимные методы широко применяются в существующем коде С# и поэтому по-прежнему являются важной составной частью С#. А поскольку анонимные методы предшествовали по явлению лямбда-выражений, то ясное представление о них позволяет лучше понять особенности лямбда-выражений. К тому же анонимные методы могут быть использо ваны в целом ряде случаев, где применение лямбда-выражений оказывается невозмож ным. Именно поэтому в этой главе рассматриваются и анонимные методы, и лямбда- выражения. Анонимные методы
Анонимный метод — один из способов создания безымянного блока кода, связан ного с конкретным экземпляром делегата. Для создания анонимного метода достаточ но указать кодовый блок после ключевого слова delegate. Покажем, как это делается, на конкретном примере. В приведенной ниже программе анонимный метод служит для подсчета от 0 до 5. // Продемонстрировать применение анонимного метода. using System; // Объявить тип делегата. delegate void CountIt; class AnonMethDemo { static void Main { // Далее следует код для подсчета чисел, передаваемый делегату // в качестве анонимного метода. CountIt count = delegate { // Этот кодовый блок передается делегату. for(int i=0; i <= 5; i++) Console.WriteLine(i); }; // обратите внимание на точку с запятой count; } }
В данной программе сначала объявляется тип делегата CountIt без параметров и с возвращаемым типом void. Далее в методе Main создается экземпляр count делегата CountIt, которому передается кодовый блок, следующий после ключевого слова delegate. Именно этот кодовый блок и является анонимным методом, кото рый будет выполняться при обращении к делегату count. Обратите внимание на то, что после кодового блока следует точка с запятой, фактически завершающая оператор объявления. Ниже приведен результат выполнения данной программы. 0 1 2 3 4 5 Передача аргументов анонимному методу
Анонимному методу можно передать один или несколько аргументов. Для этого достаточно указать в скобках список параметров после ключевого слова delegate, а при обращении к экземпляру делегата — передать ему соответствующие аргумен ты. В качестве примера ниже приведен вариант предыдущей программы, измененный с целью передать в качестве аргумента конечное значение для подсчета. // Продемонстрировать применение анонимного метода, принимающего аргумент. using System; // Обратите внимание на то, что теперь у делегата CountIt имеется параметр. delegate void CountIt (int end); class AnonMethDemo2 { static void Main { // Здесь конечное значение для подсчета передается анонимному методу. CountIt count = delegate (int end) { for(int i=0; i <= end; i++) Console.WriteLine(i); }; count(3); Console.WriteLine; count(5); } }
В этом варианте программы делегат CountIt принимает целочисленный аргумент. Обратите внимание на то, что при создании анонимного метода список параметров указывается после ключевого слова delegate. Параметр end становится доступным для кода в анонимном методе таким же образом, как и при создании именованного метода. Ниже приведен результат выполнения данной программы. 0 1 2 3 0 1 2 3 4 5 Возврат значения из анонимного метода
Анонимный метод может возвращать значение. Для этой цели служит оператор return, действующий в анонимном методе таким же образом, как и в именованном методе. Как и следовало ожидать, тип возвращаемого значения должен быть совме стим с возвращаемым типом, указываемым в объявлении делегата. В качестве при мера ниже приведен код, выполняющий подсчет с суммированием и возвращающий результат. // Продемонстрировать применение анонимного метода, возвращающего значение. using System; // Этот делегат возвращает значение. delegate int CountIt(int end); class AnonMethDemo3 { static void Main { int result; // Здесь конечное значение для подсчета перелается анонимному методу. // А возвращается сумма подсчитанных чисел. CountIt count = delegate (int end) { int sum = 0; for(int i=0; i <= end; i++) { Console.WriteLine (i); sum += i; } return sum; // возвратить значение из анонимного метода }; result = count(3); Console.WriteLine("Сумма 3 равна " + result); Console.WriteLine; result = count (5); Console.WriteLine("Сумма 5 равна " + result); } }
В этом варианте кода суммарное значение возвращается кодовым блоком, связан ным с экземпляром делегата count. Обратите внимание на то, что оператор return применяется в анонимном методе таким же образом, как и в именованном методе. Ниже приведен результат выполнения данного кода. 0 1 2 3 Сумма 3 равна 6 0 1 2 3 4 5 Сумма 5 равна 15 Применение внешних переменных в анонимных методах
Локальная переменная, в область действия которой входит анонимный метод, на зывается внешней переменной. Такие переменные доступны для использования в ано нимном методе. И в этом случае внешняя переменная считается захваченной. Захвачен ная переменная существует до тех пор, пока захвативший ее делегат не будет собран в "мусор". Поэтому если локальная переменная, которая обычно прекращает свое су ществование после выхода из кодового блока, используется в анонимном методе, то она продолжает существовать до тех пор, пока не будет уничтожен делегат, ссылаю щийся на этот метод.
Захват локальной переменной может привести к неожиданным результатам. В ка честве примера рассмотрим еще один вариант программы подсчета с суммированием чисел. В данном варианте объект CountIt конструируется и возвращается статическим методом Counter. Этот объект использует переменную sum, объявленную в охваты вающей области действия метода Counter, а не самого анонимного метода. Поэто му переменная sum захватывается анонимным методом. Метод Counter вызывается в методе Main для получения объекта CountIt, а следовательно, переменная sum не уничтожается до самого конца программы. // Продемонстрировать применение захваченной переменной. using System; // Этот делегат возвращает значение типа int и принимает аргумент типа int. delegate int CountIt(int end); class VarCapture { static CountIt Counter { int sum = 0; // Здесь подсчитанная сумма сохраняется в переменной sum. CountIt ctObj = delegate (int end) { for(int i=0; i <= end; i++) { Console.WriteLine(i); sum += i; } return sum; }; return ctObj; } static void Main { // Получить результат подсчета. CountIt count = Counter; int result; result = count(3); Console.WriteLine("Сумма 3 равна " + result); Console.WriteLine; result = count (5); Console.WriteLine("Сумма 5 равна " + result); } }
Ниже приведен результат выполнения этой программы. Обратите особое внима ние на суммарное значение. 0 1 2 3 Сумма 3 равна 6 0 1 2 3 4 5 Сумма 5 равна 21
Как видите, подсчет по-прежнему выполняется как обычно. Но обратите внимание на то, что сумма 5 теперь равна 21, а не 15! Дело в том, что переменная sum захваты вается объектом ctObj при его создании в методе Counter. Это означает, что она продолжает существовать вплоть до уничтожения делегата count при "сборке мусо ра" в самом конце программы. Следовательно, ее значение не уничтожается после воз врата из метода Counter или при каждом вызове анонимного метода, когда проис ходит обращение к делегату count в методе Main.