Г. Шилдт - Полный справочник по C# (1160789), страница 79
Текст из файла (страница 79)
События можно определять как абстрактные. Обеспечить реализацию такого события должен производный класс. Однако события, реаГлава 15. Делегаты и события425лизованные с использованием средств доступа add и remove, абстрактными быть немогут. Любое событие можно определить с помощью ключевого слова sealed. Событие может быть виртуальным, т.е.
его можно переопределить в производном классе.Рекомендации по обработке событий в среде.NET FrameworkС# позволяет профаммисту создавать события любого типа. Однако в целях компонентной совместимости со средой .NET Framework необходимо следовать рекомендациям, подготовленным Microsoft специально для этих целей. Центральное место вэтих рекомендациях занимает требование того, чтобы обработчики событий имели двапараметра. Первый должен быть ссылкой на объект, который будет генерировать событие.
Второй должен иметь тип EventArgs и содержать остальную информацию,необходимую обработчику. Таким образом, .NET-совместимые обработчики событийдолжны иметь следующую общую форму записи:void handler(object source, EventArgs arg) {Обычно параметр source передается вызывающим кодом. Параметр типаEventArgs содержит дополнительную информацию, которую в случае ненадобностиможно проигнорировать.Класс EventArgs не содержит полей, которые используются при передаче дополнительных данных обработчику; он используется в качестве базового класса, из которого можно выводить класс, содержащий необходимые поля. Но поскольку многиеобработчики обходятся без дополнительных данных, в класс EventArgs включеностатическое поле Empty, которое задает объект, не содержащий никаких данных.Ниже приведен пример, в котором создается .NET-совместимое событие.// А .NET-совместимое событие.using System;// Создаем класс, производный от класса EventArgs.c l a s s MyEventArgs : EventArgs {p u b l i c i n t eventnum;}// Объявляем делегат для события.delegate void MyEventHandler(object source,MyEventArgs arg);// Объявляем класс события,class MyEvent {s t a t i c i n t count = 0;public event MyEventHandler SomeEvent;// Этот метод генерирует SomeEvent-событие.public void OnSomeEvent() {MyEventArgs arg = new MyEventArgs();if(SomeEvent426!= null) {Часть I.
Язык С#arg.eventnum = count++;SomeEvent(this, arg);class X {public void handler(object source, MyEventArgs arg) {Console.WriteLine("Событие " + arg.eventnum +11получено объектом X . " ) ;Console.WriteLine("Источником является класс " +source + " .
" ) ;Console.WriteLine();class Y {public void handler(object source, MyEventArgs arg) {Console.WriteLine("Событие " + arg.eventnum +" получено объектом Y.");Console.WriteLine("Источником является класс " +source + " . " ) ;Console.WriteLine();class EventDernopublic staticX obi - newY ob2 = newMyEvent evt{void Main() {X() ;у();— new MyEventO;// Добавляем обработчик handler() в список событий,evt. SomeEvert +-- new MyEventHandler (obi .handler) ;evt.SomeEvent +~ new MyEventHandler(ob2.handler);// Генерируем событие,evt.OnSomeEvent();evt.OnSomeEvent();Вот как выглядят результаты выполнения этой программы:Событие 0 получено объектом X.Источником является класс MyEvent.Событие 0 получено объектом Y.Источником является класс MyEvent.Событие 1 получено объектом X.Источником является класс MyEvent.Событие I получено объектом Y.Источником является класс MyEvent.В этом примере класс MyEventArgs выводится из класса EventArgs.
В классеMyEventArgs добавлено только одно "собственное" поле — eventnum. В соответствииГлава 15. Делегаты и события427с требованиями .NET Framework делегат для обработчика событий MyEventHandlerтеперь принимает два параметра. Как разъяснялось выше, первый из них представляет собой объектную ссылку на генератор событий, а второй — ссылку на классEventArgs или производный от класса EventArgs. В данном случае здесь используется ссылка на объект типа MyEventArgs.Использование встроенного делегата EventHandlerДля многих событий параметр типа EventArgs не используется. Для упрощенияпроцесса создания кода в таких ситуациях среда .NET Framework включает встроенный тип делегата, именуемый EventHandler.
Его можно использовать для объявления обработчиков событий, которым не требуется дополнительная информация. Рассмотрим пример использования типа EventHandler.// Использование встроенного делегата EventHandler.using System;// Объявляем класс события,class MyEvent {public event EventHandler SomeEvent; // Объявление// использует делегат EventHandler.// Этот метод вызывается для генерирования// SomeEvent-событие.public void OnSomeEvent() {if(SomeEvent != null)SomeEvent(this, EventArgs.Empty);class EventDemo {static void handler(object source, EventArgs arg) {Console.WriteLine("Событие произошло.");Console.WriteLine("Источником является класс " +source + " .
" ) ;public static void Main() {MyEvent evt = new MyEvent();// Добавляем обработчик handler() в список событий,evt.SomeEvent +- new EventHandler(handler);// Генерируем событие,evt.OnSomeEvent();В данном случае параметр типа EventArgs не используется и вместо него передается объект-заполнитель EventArgs.Empty. Результаты выполнения этой программывесьма лаконичны:I Событие произошло.I Источником является класс MyEvent.428Часть I. Язык С#Учебный проект: использование событийСобытия часто используются в таких средах с ориентацией на передачу сообщений, как Windows.
В подобной среде программа просто ожидает до тех пор, пока неполучит сообщение, а затем выполняет соответствующие действия. Такая архитектурапрекрасно подходит для обработки событий в стиле языка С#, позволяя создавать обработчики событий для различных сообщений и просто вызывать обработчик при получении определенного сообщения. Например, с некоторым событием можно былобы связать сообщение, получаемое в результате щелчка левой кнопкой мыши. Тогдапосле щелчка левой кнопкой мыши все зарегистрированные обработчики будут уведомлены о приходе этого сообщения.Несмотря на то что разработка Windows-программ, в которых демонстрируется такой подход, выходит за рамки этой главы, все же обрисуем в общих чертах работуэтого механизма. В следующей программе создается обработчик событий нажатияклавиш.
Событие называется KeyPress, и при каждом нажатии клавиши оно генериуется посредством вызова метода OnKeyPress ().// Пример обработки события, связанного с нажатием// клавиши на клавиатуре.using System;// Выводим собственный класс EventArgs, который// будет хранить код клавиши,class KeyEventArgs : EventArgs {public char ch;}// Объявляем делегат для события.delegate void KeyHandler(object source, KeyEventArgs arg);// Объявляем класс события, связанного с нажатием// клавиши на клавиатуре,class KeyEvent {public event KeyHandler KeyPress;// Этот метод вызывается при нажатии// какой-нибудь клавиши,public void OnKeyPress(char key) {KeyEventArgs k = new KeyEventArgs();if(KeyPress != null) {k.ch = key;KeyPress(this, k ) ;// Класс, который принимает уведомления о нажатии клавиши,class ProcessKey {public void keyhandler(object source, KeyEventArgs arg) {Console.WriteLine("Получено сообщение о нажатии клавиши: " + arg.cn);Глава 15.
Делегаты и события429// Еще один класс, который принимает уведомления//онажатии клавиши,class CountKeys {public int count = 0 ;public void keyhandler(object source, KeyEventArgs arg) {count++;// Демонстрируем использование класса KeyEvent.c l a s s KeyEventDemo {p u b l i c s t a t i c void MainO {KeyEvent kevt = new KeyEvent();ProcessKey pk = new ProcessKey();CountKeys ck = new CountKeys();char ch;kevt.KeyPress += new KeyHandler(pk.keyhandler);kevt.KeyPress +~ new KeyHandler(ck.keyhandler);Console.WriteLine("Введите несколько символов.
" +"Дляостанова введите точку.");do {ch = (char) Console.Read();kevt.OnKeyPress(ch);} while(ch !- ' . ' ) ;Console.WriteLine("Было нажато " +сk.count + " клавиш.");При выполнении этой программы можно получить такие результаты:Введите несколько символов. Для останова введите точку.тест.Получено сообщение о нажатии клавиши: тПолучено сообщение о нажатии клавиши: еПолучено сообщение о нажатии клавиши: сПолучено сообщение о нажатии клавиши: тПолучено сообщение о нажатии клавиши: .Было нажато 5 клавиш.Эта программа начинается с выведения класса KeyEventArgs, который используется для передачи сообщения о нажатии клавиши обработчику событий. Затем делегатKeyHandler определяет обработчик для событий, связанных с нажатием клавиши наклавиатуре.
Эти события инкапсулируются в классе KeyEvent.Программа для обработки нажатий клавиш создает два класса: ProcessKey иCountKeys. Класс ProcessKey включает обработчик с именем keyhandler (), который отображает сообщение о нажатии клавиши. Класс CountKeys предназначен дляхранения текущего количества нажатых клавиш. В методе MainO создается объекткласса KeyEvent. Затем создаются объекты классов ProcessKey и CountKeys, aссылки на их методы keyhandler () добавляются в список вызовов, реализуемый спомощью событийного объекта kevt.KeyPress. Затем начинает работать цикл, в котором при каждом нажатии клавиши вызывается метод kevt .OnKeyPress ( ) , в результате чего зарегистрированные обработчики уведомляются о событии.430Часть I.
Язык С #Полныйсправочник поПространства имен,препроцессор и компоновочныефайлыВэтой главе рассматриваются три С#-средства, которые позволяют влиять наорганизацию и доступность программы. Речь пойдет о пространствах имен,препроцессоре и компоновочных файлах.Пространства именО пространствах имен кратко упоминалось в главе 2, поскольку это одно из основополагающих понятий С#: каждая Сопрограмма так или иначе использует некоторое пространство имен. До сих пор мы не затрагивали эту тему, поскольку С# автоматически предоставляет программе пространство имен по умолчанию.
Таким образом,программы, приведенные в предыдущих главах, просто использовали стандартноепространство имен. Но реальным программам придется создавать собственные иливзаимодействовать с другими пространствами имен. Поэтому настало время поговорить о них более подробно.Пространство имен определяет декларативную область, которая позволяет отдельнохранить множества имен. По существу, имена, объявленные в одном пространствеимен, не будут конфликтовать с такими же именами, объявленными в другом.
Библиотека .NET Framework (которая является С#-библиотекой) использует пространствоимен System. Поэтому в начало каждой программы мы включали следующую инстукцию:using System;^Как было показано в главе 14, классы ввода-вывода определяются внутри пространства имен, подчиненного System, и именуемого System. 10. Существуют идругие пространства имен, подчиненные System, которые включают иные части С#библиотеки.Возникновение пространств имен продиктовано самой жизнью, поскольку в течение последних лет для программирования характерен взрывоподобный рост количества имен переменных, методов, свойств и классов, которые используются в библиотечных процедурах, приложениях сторонних изготовителей ПО и программах, написанных отдельными программистами. Без использования пространств имен все этиимена боролись бы за место "под солнцем" в глобальном пространстве имен, чтопривело бы к росту числа конфликтов.