Лекция 11(делег).ppt
- Количество слайдов: 34
Делегаты Делегат — это объект, который может ссылаться на метод. Создавая делегат, вы по сути создаете объект, который может содержать ссылку на метод. Более того, этот метод можно вызвать посредством соответствующей ссылки. Таким образом, делегат может вызывать метод, на который он ссылается. Несмотря на то что метод не является объектом, он имеет отношение к физической области памяти, а адрес его точки входа — это адрес, к которому происходит обращение при вызове метода. Этот адрес можно присвоить делегату. И, если делегат ссылается на метод, этот метод можно вызвать посредством данного делегата Во время выполнения программы один и тот же делегат можно использовать для вызова различных методов, заменив метод, на который ссылается этот делегат. Метод, который будет вызван делегатом, определяется во время работы программы.
Форма объявления делегата delegate тип_возврата имя(список_параметров); Делегат может вызывать только такие методы, у которых тип возвращаемого значения и список параметров (т. е. его сигнатура) совпадают с соответствующими элементами объявления делегата. Делегат может вызывать либо метод экземпляра класса, связанный с объектом, или статический метод, связанный с классом.
Описание делегата delegate void My. Delegate(string s); Описывая делегат, необходимо понимать, что вводится новый тип, а именно, класс-потомок Multicast. Delegate. То есть конструкция, приведенная выше, на самом деле порождает примерно такой код: class My. Delegate : Multicast. Delegate{. . . } Компилятор проводит такую трансляцию неявно, дабы не пугать рядовых программистов ужасами внутренней архитектуры делегатов. Чтобы воспользоваться делегатом, необходимо создать экземпляр данного класса, который бы указывал на нужный нам метод. Сам по себе делегат является типом и никаких ссылок на метод содержать не может, а соответственно, не может и использоваться для обращения к ним. Для этого необходимо использовать экземпляр делегата. Создание экземпляра делегата просто, поскольку компилятор уже подготовил все необходимое. Он создал специальный конструктор, которому нужно передать указатель на метод, а со всем остальным разберется среда исполнения.
Создание экземпляра делегата My. Delegate del = new My. Delegate(My. Handler); My. Delegate —тип делегата, del — экземпляр делегата, который будет создан в результате выполнения конструкции, My. Handler — метод, на который будет ссылаться этот делегат. Соответственно, после создания экземпляра делегата можно обращаться к методам, на которые он ссылается. del( «Привет, мир!");
Пример (статический метод) using System; class App{ delegate void My. Delegate(string s); static void My. Handler(string s) {Console. Write. Line(s); } static void Main() { My. Delegate del = new My. Delegate(My. Handler); del( «Привет"); } }}
Вызов экземплярных методов. Единственным отличием от обращения к статическим функциям будет способ создания делегата. Необходимо будет указать ссылку на объект, который будет использоваться при вызове метода через данный делегат. My. Deleg del = new My. Deleg(sc. My. Handler); Где sc является ссылкой на экземпляр объекта, который должен использоваться при обращении к методу My. Handler.
Пример class Some. Class { // Поле связано с экземпляром данного типа. public string Some. Field; public void My. Handler(string s) { Console. Write. Line(Some. Field + s); } public void Instance. Method() { Console. Write. Line("Inst. Method was called – Привет!"); } };
Пример прод. delegate void My. Delegate(string s); delegate void Some. Delegate(); static void Main(string[] args) { // Создадим экземпляр тестового класса. Some. Class sc = new Some. Class(); // Создадим экземпляр делегата, содержащего, // помимо ссылки на сам метод, также ссылку // на объект, для которого будет вызван метод. My. Delegate del = new My. Delegate(sc. My. Handler); // Изменяем значение поля тестового объекта. sc. Some. Field = "Hello, World!"; // Вызовем метод через делегат. del(" - from Instance Delegate"); // Эквивалентен следующему вызову // sc. My. Handler(" - from Instance Delegate");
Пример конец. // Снова изменим значение поля тестового объекта. sc. Some. Field = "Good bye, World!"; // Снова обратимся к методу. del(" - from Instance Delegate"); Console. Write. Line(del. Method. To. String()); Console. Write. Line(del. Target. Get. Type(). To. String()); Console. Write. Line(del. Method. To. String()); del. Target. Get. Type(). To. String()); // Информация по методу, на который ссылается делегат. Console. Write. Line("Method = {0}", del. Method. To. String()); }
Многоадресатная передача — это способность создавать список вызовов (или цепочку вызовов) методов, которые должны автоматически вызываться при вызове делегата. Добавление методов в цепочку оператор "+=". Удаление метода из цепочки оператор" - = ". Можно также для добавления и удаления методов использовать в отдельности операторы "+", " - " и "=", но чаще применяются операторы "+=" и "-=". Делегат с многоадресатной передачей имеет одно ограничение: он должен возвращать тип void.
Пример using System; // Объявляем делегат. delegate void str. Mod(ref string str); class String. Ops { static void replace. Spaces(ref string a) { a = a. Replace(‘ ‘, ‘-‘ ) ; } static void remove. Spaces(ref string a) { …. . } static void reverse(ref string a) { ……. }
Пример прод. public static void Main() { str. Mod str. Op; str. Mod replace. Sp = new str. Mod(replace. Spaces); str. Mod remove. Sp = new str. Mod(remove. Spaces); str. Mod reverse. Str = new str. Mod(reverse); string str = "Это простой тест. "; str. Op+= replace. Sp; str. Op+= reverse. Str; // Вызов делегата с многоадресатной передачей. str. Op(ref str) Console. Write. Line("Результирующая строка: " + str); str. Op -= replace. Sp; str. Op += remove. Sp; str = "Это простой тест. "; str. Op(ref str); Console. Write. Line("Результирующая строка: " + str); Console. Write. Line(); …
Зачем нужны делегаты • Обеспечивают поддержку функционирования событий. • Позволяют во время выполнения программы выполнить метод, который точно не известен в период компиляции. (Опора для создания плагинов).
События Событие — это по автоматическое уведомление о выполнении некоторого действия Объект, которому необходима информация о некотором событии, регистрирует обработчик для этого события. Когда ожидаемое событие происходит, вызываются все зарегистрированные обработчики. Обработчики событий представляются делегатами. События — это члены класса, которые объявляются с использованием ключевого слова event. Наиболее распространенная форма объявления события: event событийный_делегат объект; событийный_делегат- имя делегата, используемого для поддержки объявляемого события элемент объект —имя событийного объекта.
Пример using System; delegate void My. Event. Handler(); // Объявляем делегат для события, class My. Event {// Объявляем класс события public event My. Event. Handler Some. Event; public void On. Some. Event() {// вызов для генерирования события if(Some. Event != null) Some. Event(); } class Event. Demo { static void handler() {// Обработчик события Console. Write. Line("Произошло событие. "); } public static void Main() { My. Event evt = new My. Event(); evt. Some. Event += new My. Event. Handler(handler); // Генерируем событие, evt. On. Some. Event(); …
Пример события для многоадресатной передачи Подобно делегатам события могут предназначаться для многоадресатной передачи. В этом случае на одно уведомление о событии может отвечать несколько объектов -------- using System; // Объявляем делегат для события, delegate void My. Event. Handler(); // Объявляем класс события, class My. Event { public event My. Event. Handler Some. Event; // Этот метод вызывается для генерирования события, public void On. Some. Event() { if(Some. Event != null) Some. Event(); }
Пример прод. class X { public void Xhandler() { Console. Write. Line("Событие, полученное объектом X. "); } } class Y { public void Yhandler () { Console. Write. Line("Событие, полученное объектом Y. ); } } class Event. Demo { static void handler() { Console. Write. Line( "Событие, полученное классом Event. Demo. "); }
Пример прод. public static void Main() { My. Event evt = new My. Event(); X x. Ob = new X(); Y y. Ob = new Y () ; evt. Some. Event += new My. Event. Handler(handler); evt. Some. Event += new My. Event. Handler(x. Ob. Xhandler); evt. Some. Event += new My. Event. Handler(y. Ob. Yhandler); // My. Event. Handler evy = new //My. Event. Handler(y. Ob. Yhandler); evt. On. Some. Event(); // Генерируем событие, Console. Write. Line(); // Удаляем один обработчик. evt. Some. Event -= new My. Event. Handler(x. Ob. Xhandler); // evt. Some. Event -= evy; evt. On. Some. Event(); }}
Сравнение методов экземпляров классов со статическими методами. • Если в качестве обработчика используется статический метод, уведомление о событии применяется к классу (и неявно ко всем объектам этого класса). • Если же в качестве обработчика событий используется метод экземпляра класса, события посылаются к конкретным экземплярам этого класса. Следовательно, каждый объект класса, который должен получать уведомление о событии, необходимо регистрировать в отдельности.
Пример методов экземпляров using System; // Объявляем делегат для события delegate void My. Event. Handler(); // Объявляем класс события class My. Event { public event My. Event. Handler Some. Event; // Этот метод вызывается для генерирования события, public void On. Some. Event() { if(Some. Event != null) Some. Event(); class X { int id; public X(int x) { id = x; } // Метод - обработчик событий public void Xhandler() { Console. Write. Line("Событие принято объектом " + id); } }
Пример методов экземпляров оконч. class Event. Demo { public static void Main() { My. Event evt = new My. Event(); X o 1 = new X(1) ; X o 2 = new X(2) ; X o 3 = new X(3); evt. Some. Event += new My. Event. Handler(o 1. Xhandler); evt. Some. Event += new My. Event. Handler(o 2. Xhandler); evt. Some. Event += new My. Event. Handler(o 3. Xhandler); // Генерируем событие, evt. On. Some. Event() ; }
Пример статического метода using System; // Объявляем делегат для события. delegate void My. Event. Handler(); // Объявляем класс события, class My. Event { public event My. Event. Handler Some. Event; // Этот метод вызывается для генерирования события, public void On. Some. Event() { if(Some. Event != null) Some. Event(); }} class X { /* Это статический метод, используемый в качестве обработчика события. */ public static void Xhandler() { Console. Write. Line("Событие получено классом. "); } }
Пример статического метода оконч. class Event. Demo { public static void Main() { My. Event evt = new My. Event(); evt. Some. Event += new My. Event. Handler(X. Xhandler); // Генерируем событие, evt. On. Some. Event(); }} --------------------------------- Обратите внимание на то, что в программе не создается ни одного объекта типа х. Но, поскольку handler () — статический метод класса X, его можно связать с событием Some. Event и обеспечить его выполнение при вызове метода On. Some. Event ().
Использование событийных средств доступа Предусмотрены две формы записи инструкций, связанных с событиями. Форма, используемая в предыдущих примерах, обеспечивала создание событий, которые автоматически управляют списком вызова обработчиков, включая такие операции, как добавление обработчиков в список и их удаление Таким образом, можно было не беспокоиться о реализации операций по управлению этим списком. Однако можно и самим организовать ведение списка обработчиков событий, чтобы, например, реализовать специализированный механизм хранения событий.
Форма обработчиков event событийный_делегат имя__события { add { // Код добавления события в цепочку событий. } remove { // Код удаления события из цепочки событий. } } ---------------------------- Средство доступа add вызывается в случае, когда с помощью оператора "+=" в цепочку событий добавляется новый обработчик, а средство доступа remove вызывается, когда с помощью оператора " - = " из цепочки событий удаляется новый обработчик.
Реализация Средство доступа add или remove при вызове получает обработчик, который необходимо добавить или удалить, в качестве параметра. Этот параметр, как и в случае использования других средств доступа, называется value. При реализации средств доступа add и remove можно задать собственную схему хранения обработчиков событий. Например, для этого вы могли бы использовать массив, стек или очередь.
Области применимости События можно определять в интерфейсах. "Поставкой" событий должны заниматься соответствующие классы. События можно определять как абстрактные. Обеспечить реализацию такого события должен производный класс. Однако события, реализованные с использованием средств доступа add и remove, абстрактными быть не могут. Любое событие можно определить с помощью ключевого слова sealed. Событие может быть виртуальным, т. е. его можно переопределить в производном классе.
Рекомендации по обработке событий в среде. NET Framework С# позволяет программисту создавать события любого типа. Однако в целях компонентной совместимости со средой. NET Framework необходимо следовать рекомендациям, подготовленным Microsoft специально для этих целей. Центральное место в этих рекомендациях занимает требование того, чтобы обработчики событий имели два параметра. Первый должен быть ссылкой на объект, который будет генерировать событие. Второй должен иметь тип Event. Args и содержать остальную информацию, необходимую обработчику.
Форма записи обработчика void handler(object source, Event. Args arg) {…} Параметр source передается вызывающим кодом. Параметр типа Event. Args содержит дополнительную информацию, которую в случае ненадобности можно проигнорировать. Класс Event. Args не содержит полей, которые используются при передаче дополнительных данных обработчику; он используется в качестве базового класса, из которого можно выводить класс, содержащий необходимые поля. Поскольку многие обработчики обходятся без дополнительных данных, в класс Event. Args включено статическое поле Empty, которое задает объект, не содержащий никаких данных.
Использование встроенного делегата Event. Handler. using System; // Объявляем класс события, class My. Event { public event Event. Handler Some. Event; public void On. Some. Event() { if(Some. Event != null) Some. Event(this, Event. Args. Empty); class Event. Demo { static void handler(object source, Event. Args arg) { Console. Write. Line("Событие произошло. "); Console. Write. Line("Источником является класс " +source + ". " ) ; public static void Main() { My. Event evt = new My. Event(); evt. Some. Event +- new Event. Handler(handler); evt. On. Some. Event(); }}
Использование событий События часто используются в таких средах с ориентацией на передачу сообщений, как Windows. В подобной среде программа просто ожидает до тех пор, пока не получит сообщение, а затем выполняет соответствующие действия. Такая архитектура прекрасно подходит для обработки событий в стиле языка С#, позволяя создавать об- работчики событий для различных сообщений и просто вызывать обработчик при получении определенного сообщения. Например, с некоторым событием можно было бы связать сообщение, получаемое в результате щелчка левой кнопкой мыши. Тогда после щелчка левой кнопкой мыши все зарегистрированные обработчики будут уведомлены о приходе этого сообщения.
Пример using System; // Выводим собственный класс. Event. Args, который будет хранить код клавиши, class Key. Event. Args : Event. Args { public char ch; } delegate void Key. Handler(object source, Key. Event. Args arg); // класс события, связанного с нажатием клавиши на клавиатуре, class Key. Event { public event Key. Handler Key. Press; // Этот метод вызывается при нажатии клавиши public void On. Key. Press(char key) { Key. Event. Args k = new Key. Event. Args(); if(Key. Press != null) { k. ch = key; Key. Press(this, k ); }
Пример прод. // Класс, который принимает уведомления о нажатии клавиши, class Process. Key { public void keyhandler(object source, Key. Event. Args arg) { Console. Write. Line(Клавиша: " + arg. cn); // Еще один класс, который принимает уведомления //о нажатии клавиши, class Count. Keys { public int count = 0 ; public void keyhandler(object source, Key. Event. Args arg) { count++; }}
Пример оконч. // Демонстрируем использование класса Key. Event. class Key. Event. Demo { public static void Main() { Key. Event kevt = new Key. Event(); Process. Key pk = new Process. Key(); Count. Keys ck = new Count. Keys(); char ch; kevt. Key. Press += new Key. Handler(pk. keyhandler); kevt. Key. Press += new Key. Handler(ck. keyhandler); Console. Write. Line("Для останова введите точку. "); do { ch = (char) Console. Read(); kevt. On. Key. Press(ch); } while(ch != '. ' ) ; Console. Write. Line("Было нажато " + сk. count + " клавиш. "); }}
Лекция 11(делег).ppt