Г. Шилдт - Полный справочник по C# (1160789), страница 77
Текст из файла (страница 77)
Язык С#Console.WriteLine("Результирующая строка: " + str);Console.WriteLine();// Удаляем метод замены пробелов и// добавляем метод их удаления.strOp -= replaceSp;strOp += removeSp;str = "Это простой тест."; // Восстановление// исходной строки.// Вызов делегата с многоадресатной передачей.strOp(ref str);Console.WriteLine("Результирующая строка: " + str);Console.WriteLine();Вот как выглядят результаты выполнения этой программы:Замена пробелов дефисами.Реверсирование строки.Результирующая строка: .тсет-йотсорп-отЭРеверсирование строки.Удаление пробелов.Результирующая строка: .тсетйотсорпотЭВ методе Main() создаются четыре экземпляра делегата.
Первый, strOp, имеетnull-значение. Три других ссылаются на методы модификации строк. Затем организуется делегат для многоадресатной передачи, который вызывает методы removeSpaces ()и r e v e r s e ( ) . Это достигается благодаря следующим строкам программы:strOp = repiaceSp;strOp += reverseStr;IСначала делегату strOp присваивается ссылка replaceSp. Затем, с помощью оператора "+=", в цепочку вызовов добавляется ссылка r e v e r s e S t r . При вызове делегатаs t r o p в этом случае вызываются оба метода, заменяя пробелы дефисами и реверсируястроку.Затем при выполнении строки программыI strOp -== replaceSp;из цепочки вызовов удаляется ссылка replaceSp, а с помощью строки1 strOp += removeSp;в цепочку вызовов добавляется ссылка removeSp.Затем делегат StrOp вызывается снова.
На этот раз из исходной строки удаляютсяпробелы, после чего она реверсируется.Цепочки вызовов, организованные с помощью делегата, — мощный механизм, который позволяет определять набор методов, выполняемых "единым блоком". Как будет показано ниже, цепочки делегатов имеют особое значение для событий.Класс System.DelegateВсе делегаты представляют собой классы, которые неявным образом выводятся изкласса System. Delegate. Обычно его члены не используются напрямую, и в этойкниге не показано явное использование класса System. Delegate. Все же в некоторых ситуациях его члены могут оказаться весьма полезными.Глава 15. Делегаты и события415Назначение делегатовНесмотря на то что предыдущие примеры программ продемонстрировали, "как"работают делегаты, они не содержали ответа на вопрос "зачем это нужно?". Так вот,делегаты используются по двум основным причинам.
Во-первых, как будет показано вследующем разделе, делегаты обеспечивают поддержку функционирования событий.Во-вторых, делегаты позволяют во время выполнения программы выполнить метод,который точно не известен в период компиляции. Эта возможность особенно полезна, когда нужно создать йболочку, к которой могли бы подключаться программныекомпоненты.
Например, представьте графическую программу (наподобие стандартнойутилиты Windows Paint). Используя делегат, можно было бы разрешить пользователюподключать специальные цветные светофильтры или анализаторы изображений. Более того, пользователь мог бы создавать "свои" последовательности этих фильтровили анализаторов.
С помощью делегатов организовать такой алгоритм очень легко.СобытияНа основе делегатов построено еще одно важное средство С#: событие (event). Событие — это по сути автоматическое уведомление о выполнении некоторого действия.События работают следующим образом. Объект, которому необходима информация онекотором событии, регистрирует обработчик для этого события. Когда ожидаемоесобытие происходит, вызываются все зарегистрированные обработчики. А теперьвнимание: обработчики событий представляются делегатами.События — это члены класса, которые объявляются с использованием ключевогослова 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() {416Часть I.
Язык С#Console.WriteLine("Произошло событие.");public static void Main() {MyEvent evt = new MyEvent();// Добавляем метод handler() в список события,evt.SomeEvent += new MyEventHandler(handler);// Генерируем событие,evt.OnSomeEvent();При выполнении программа отображает следующие результаты:1 Произошло событие.Несмотря на простоту, программа содержит все элементы, необходимые для надлежащей обработки события.
Рассмотрим их по порядку.Программа начинается с такого объявления делегата для обработчика события:I d e l e g a t e void MyEventHandler();Все события активизируются посредством делегата. Следовательно, событийныйделегат определяет сигнатуру для события. В данном случае параметры отсутствуют,однако событийные параметры разрешены. Поскольку события обычно предназначены для многоадресатной передачи, они должны возвращать значение типа void.Затем создается класс события MyEvent. При выполнении следующей строки кода,принадлежащей этому классу, объявляется событийный объект SomeEvent:I p u b l i c event MyEventHandler SomeEvent;Обратите внимание на синтаксис.
Именно так объявляются события всех типов.Кроме того, внутри класса MyEvent объявляется метод OnSomeEvent (), который вэтой программе вызывается, чтобы сигнализировать о событии. (Другими словами,этот метод вызывается, когда происходит событие.) Как показано в следующем фрагменте кода, он вызывает обработчик события посредством делегата SomeEvent.if(SomeEvent != null)SomeEvent();IОбратите внимание на то, что обработчик события вызывается только в том случае, если делегат SomeEvent не равен null-значению. Поскольку другие части программы, чтобы получить уведомлении о событии, должны зарегистрироваться, можносделать так, чтобы метод OnSomeEvent () был вызван до регистрации любого обработчика события. Чтобы предотвратить вызов null-объекта, событийный делегат необходимо протестировать и убедиться в том, что он не равен пи 11-значению.Внутри класса EventDemo создается обработчик события h a n d l e r ().
В этом примере обработчик события просто отображает сообщение, но ясно, что другие обработчики могли бы выполнять более полезные действия. Как показано в следующемфрагменте кода, в методе Main() создается объект класса MyEvent, а методh a n d l e r () регистрируется в качестве обработчика этого события.MyEvent evt = new MyEvent();// Добавляем метод h a n d l e r ( ) в список события,evt.SomeEvent += new MyEventHandler(handler);Обратите внимание на то, что обработчик добавляется в список с использованиемсоставного оператора "+=". Следует отметить, что события поддерживают только операторы "+=" и " - = " .
В нашем примере метод h a n d l e r () является статическим, но вГлава 15. Делегаты и события417общем случае обработчики событий могут быть методами экземпляров классов. Наконец, при выполнении следующей инструкции "происходит" событие, о котором мытак много говорили.I/ Генерируем событие.evt.OnSomeEvent();IПри вызове метода OnSomeEvent () вызываются все зарегистрированные обработчики событий. В данном случае зарегистрирован только один обработчик, но, как выувидите в следующем разделе, их могло бы быть и больше.Пример события для многоадресатной передачиПодобно делегатам события могут предназначаться для многоадресатной передачи.В этом случае на одно уведомление о событии может отвечать несколько объектов,рассмотрим пример.// Демонстрация использования события, предназначенного// для многоадресатной передачи.using System;// Объявляем делегат для события,delegate void MyEventHandler();// Объявляем класс события,class MyEvent {public event MyEventHandler SomeEvent;// Этот метод вызывается для генерирования события,public void OnSomeEvent() {if(SomeEvent != null)SomeEvent();class X {public void XhandlerO {Console.WriteLine("Событие, полученное объектомX.");class Y {public void YhandlerO {Console.WriteLine("Событие, полученное объектом Y .
) ;class EventDemo {static void handler() {Console.WriteLine("Событие, полученное классом EventDemo.");public staticMyEvent evtX xOb = newY yOb = new418void Main() {= new MyEvent();X();Y () ;Часть I. Язык С#// Добавляем обработчики в список события,evt.SomeEvent += new MyEventHandler(handler);evt.SomeEvent += new MyEventHandler(xOb.Xhandler);evt.SomeEvent += new MyEventHandler(yOb.Yhandler);// Генерируем событие,evt.OnSomeEvent();Console.WriteLine();// Удаляем один обработчик.evt.SomeEvent -= new MyEventHandler(xOb.Xhandler);evt.OnSomeEvent();}}Результаты выполнения этой программы имеют следующий вид:Событие, полученное классом EventDemo.Событие, полученное объектом X.Событие, полученное объектом Y.Событие, полученное классом EventDemo.Событие, полученное объектом Y.В этом примере создается два дополнительных класса X и Y, в которых также определяются обработчики событий, совместимые с сигнатурой делегата MyEventHandler.Следовательно, эти обработчики могут стать частью цепочки событийных вызовов.Обратите внимание на то, что обработчики в классах х и Y не являются статическими.Это значит, что сначала должны быть созданы объекты каждого класса, после чего вцепочку событийных вызовов должен быть добавлен обработчик, связанный с каждымэкземпляром класса.
Различие между статическими обработчиками и обработчикамиэкземпляров классов рассматривается в следующем разделе.Сравнение методов экземпляров классов со статическимиметодами, используемыми в качестве обработчиков событийНесмотря на то что и методы экземпляров классов, и статические методы могутслужить обработчиками событий, в их использовании в этом качестве есть существенные различия. Если в качестве обработчика используется статический метод, уведомление о событии применяется к классу (и неявно ко всем объектам этого класса). Если же в качестве обработчика событий используется метод экземпляра класса, события посылаются к конкретным экземплярам этого класса. Следовательно, каждыйобъект класса, который должен получать уведомление о событии, необходимо регистрировать в отдельности. На практике в большинстве случаев "роль" обработчиков событий "играют" методы экземпляров классов, но, безусловно, все зависит от конкретной ситуации.