Полное руководство. С# 4.0
Шрифт:
При выполнении этого кода получается следующий результат. Факториал 3 равен 6 Факториал 5 равен 120
В приведенном выше примере обратите внимание на то, что в теле блочного лямбда-выражения объявляется переменная r, организуется цикл for и используется оператор return. Все эти элементы вполне допустимы в блочном лямбда-выражении. И в этом отношении оно очень похоже на анонимный метод. Следовательно, многие анонимные методы могут быть преобразованы в блочные лямбда-выражения при обновлении унаследованного кода. И еще одно замечание: когда в блочном лямбда- выражении встречается оператор return, он просто обусловливает возврат из лямбда- выражения, но не возврат из охватывающего метода.
И в заключение рассмотрим еще один пример, демонстрирующий блочное лямбда- выражение в действии. Ниже приведен вариант первого примера из этой главы, изме ненного
Результат выполнения кода этого примера оказывается таким же, как и в первом примере применения делегатов. Замена пробелов дефисами. Результирующая строка: Это-простой-тест. Удаление пробелов. Результирующая строка: Этопростойтест. Обращение строки. Результирующая строка: .тсет йотсорп отЭ События
Еще одним важным средством С#, основывающимся на делегатах, является собы тие. Событие, по существу, представляет собой автоматическое уведомление о том, что произошло некоторое действие. События действуют по следующему принципу: объект, проявляющий интерес к событию, регистрирует обработчик этого события. Когда же событие происходит, вызываются все зарегистрированные обработчики это го события. Обработчики событий обычно представлены делегатами.
События являются членами класса и объявляются с помощью ключевого слова event. Чаще всего для этой цели используется следующая форма: event делегат_события имя_события;
где делегатсобытия обозначает имя делегата, используемого для поддержки собы тия, а имясобытия — конкретный объект объявляемого события.
Рассмотрим для начала очень простой пример. // Очень простой пример, демонстрирующий событие. using System; // Объявить тип делегата для события. delegate void MyEventHandler; // Объявить класс, содержащий событие. class MyEvent { public event MyEventHandler SomeEvent; // Этот метод вызывается для запуска события. public void OnSomeEvent { if(SomeEvent != null) SomeEvent; } } class EventDemo { // Обработчик события. static void Handler { Console.WriteLine("Произошло событие"); } static void Main { MyEvent evt = new MyEvent; // Добавить метод Handler в список событий. evt.SomeEvent += Handler; // Запустить событие. evt.OnSomeEvent; } }
Вот какой результат получается при выполнении этого кода. Произошло событие
Несмотря на всю свою простоту, данный пример кода содержит все основные эле менты, необходимые для обработки событий. Он начинается с объявления типа деле гата для обработчика событий, как показано ниже. delegate void MyEventHandler;
Все события активизируются с помощью делегатов. Поэтому тип делегата события определяет возвращаемый тип и сигнатуру для события. В данном случае параметры события отсутствуют, но их разрешается указывать.
Далее создается класс события MyEvent. В этом классе объявляется событие SomeEvent в следующей строке кода. public event MyEventHandler SomeEvent;
Обратите внимание на синтаксис этого объявления. Ключевое слово event уведом ляет компилятор о том, что объявляется событие.
Кроме того, в классе MyEvent объявляется метод OnSomeEvent, вызываемый для сигнализации о запуске события. Это означает,
что он вызывается, когда происходит событие. В методе OnSomeEvent вызывается обработчик событий с помощью деле гата SomeEvent. if(SomeEvent != null) SomeEvent;Как видите, обработчик вызывается лишь в том случае, если событие SomeEvent не является пустым. А поскольку интерес к событию должен быть зарегистрирован в дру гих частях программы, чтобы получать уведомления о нем, то метод OnSomeEvent может быть вызван до регистрации любого обработчика события. Но во избежание вызова по пустой ссылке делегат события должен быть проверен, чтобы убедиться в том, что он не является пустым.
В классе EventDemo создается обработчик событий Handler. В данном простом примере обработчик событий просто выводит сообщение, но другие обработчики могут выполнять более содержательные функции. Далее в методе Main создается объект класса события MyEvent, a Handler регистрируется как обработчик этого события, добавляемый в список. MyEvent evt = new MyEvent; // Добавить метод Handler в список событий. evt.SomeEvent += Handler;
Обратите внимание на то, что обработчик добавляется в список с помощью опера тора +=. События поддерживают только операторы += и -=. В данном случае метод Handler является статическим, но в качестве обработчиков событий могут также служить методы экземпляра.
И наконец, событие запускается, как показано ниже. // Запустить событие. evt.OnSomeEvent;
Вызов метода OnSomeEvent приводит к вызову всех событий, зарегистрирован ных обработчиком. В данном случае зарегистрирован только один такой обработчик, но их может быть больше, как поясняется в следующем разделе. Пример групповой адресации события
Как и делегаты, события поддерживают групповую адресацию. Это дает возмож ность нескольким объектам реагировать на уведомление о событии. Ниже приведен пример групповой адресации события. // Продемонстрировать групповую адресацию события. using System; // Объявить тип делегата для события. delegate void MyEventHandler; // Объявить делегат, содержащий событие. class MyEvent { public event MyEventHandler SomeEvent; // Этот метод вызывается для запуска события. public void OnSomeEvent { if(SomeEvent != null) SomeEvent; } } class X { public void Xhandler { Console.WriteLine("Событие получено объектом класса X"); } } class Y { public void Yhandler { Console.WriteLine("Событие получено объектом класса Y"); } } class EventDemo2 { static void Handler { Console.WriteLine("Событие получено объектом класса EventDemo"); } static void Main { MyEvent evt = new MyEvent; X xOb = new X; Y yOb = new Y; // Добавить обработчики в список событий. evt.SomeEvent += Handler; evt.SomeEvent += xOb.Xhandler; evt.SomeEvent += yOb.Yhandler; // Запустить событие. evt.OnSomeEvent; Console.WriteLine; // Удалить обработчик. evt.SomeEvent -= xOb.Xhandler; evt.OnSomeEvent; } }
При выполнении кода этого примера получается следующий результат. Событие получено объектом класса EventDemo Событие получено объектом класса X Событие получено объектом класса Y Событие получено объектом класса EventDemo Событие получено объектом класса Y
В данном примере создаются два дополнительных класса, X и Y, в которых также определяются обработчики событий, совместимые с делегатом MyEventHandler. Поэтому эти обработчики могут быть также включены в цепочку событий. Обратите внимание на то, что обработчики в классах X и Y не являются статическими. Это озна чает, что сначала должны быть созданы объекты каждого из этих классов, а затем в цепочку событий должны быть введены обработчики, связанные с их экземплярами. Об отличиях между обработчиками экземпляра и статическими обработчиками речь пойдет в следующем разделе. Методы экземпляра в сравнении со статическими методами в качестве обработчиков событий
Методы экземпляра и статические методы могут быть использованы в качестве обработчиков событий, но между ними имеется одно существенное отличие. Когда статический метод используется в качестве обработчика, уведомление о событии рас пространяется на весь класс. А когда в качестве обработчика используется метод эк земпляра, то события адресуются конкретным экземплярам объектов. Следовательно, каждый объект определенного класса, которому требуется получить уведомление о со бытии, должен быть зарегистрирован отдельно. На практике большинство обработ чиков событий представляет собой методы экземпляра, хотя это, конечно, зависит от конкретного приложения. Рассмотрим применение каждой из этих двух разновидно стей методов в качестве обработчиков событий на конкретных примерах.