Слайд 1События
Событие – средство , позволяющее объекту (или классу) послать во внешний
для объекта или класса мир сообщение о переходе в некоторое новое состояние или о получении сообщения из внешнего источника.
Посылка сообщения оформляется как оператор в теле некоторого метода.
Синтаксис оператора посылки:
имя_события(аргументы для делегата);
Объявление события может размещаться в классе или интерфейсе.
класс
Событие – это член класса (или его объекта), вводимый объявлением:
модификаторы event имя_делегата имя_события;
модификаторы: abstract, new, override, static, virtual, public, protected, private, internal.
Слайд 2имя _события - идентификатор, выбираемый программистом в качестве названия конкретного члена,
называемого переменной события.
имя_делегата – имя делегата-типа(событийный делегат). Он должен представлять событию те методы, которые будут вызываться в ответ на посылку сообщения о событии.
Cобытие – это член класса, имеющий тип делегата.
Пример
Определить статический метод, посылающий через каждую секунду сообщение о событии.
В рассмотренном примере делегат объявлен вне классов и все методы статические. Таким образом, с помощью механизма событий взаимодействуют не объекты, а методы одного класса.
Слайд 3В технологии Windows – программирования принято говорить, что объект (в данном
примере статический метод класса) публикует события, посылая сообщения о них. Другие объекты ( в данном примере – статические методы) могут подписаться на них.
Подписка на получение сообщений о событии в языке C# предусматривает следующие действия:
создание экземпляра того делегата, на который настроено событие;
подключение экземпляра делегата к событию.
Обычно эти два действия объединяют в одном операторе следующего формата:
имя_события += new имя делегата(имя метода);
Условие применимости подписки на событие – наличие и доступность метода, который будет вызван для обработки события.
Имя этого метода используется в качестве аргумента конструктора делегата. На одно событие могут быть подписаны несколько методов, для каждого из которых нужно использовать свой оператор приведенного вида.
Слайд 4using System;
delegate void TimeHandler();
class Test_cs {
static event TimeHandler onTime;
static void run()
{
Console.WriteLine("exit ctrl + c");
while (true)
onTime();
System.Threading.Thread.Sleep(1000);
}
Примечание:
Sleep() –статический метод класса Thread из пространства имен System.Threading
Слайд 5class Program
{
static void Main(string[] args)
{
onTime += new TimeHandler(one);
onTime += new TimeHandler(two);
run();
}
static void one() {
string newTime = DateTime.Now.ToString();
Console.Write("\t\t\t{0}", newTime);
}
static int count = 0;
static void two() {
Console.Write("\r{0}", count++);
}
}
}
Слайд 6Более общий случай – событие создается объектом, а в других объектах
( в объектах других классов) имеется возможность реагировать на эти события.
using System;
//объявление делегата для события
delegate void MyEventHandler();
//объявление класса события
class MyEvent
{
public event MyEventHandler SomeEvent;//объект
// вызывается для генерации события
public void OnSomeEvent()
{
if (SomeEvent != null)
SomeEvent();
}
}
Слайд 7class EventDemo
{
// обработчик события
static void Handler()
{
Console.WriteLine(" событие произошло");
}
public static void Main(string[] args)
{
MyEvent evt = new MyEvent();
// добавление Handler() в список обработчиков
evt.SomeEvent += Handler; //преобразование метода в делегат
//генерация события
evt.OnSomeEvent();
}
}
Слайд 8Пример множественной адресации события
Как и делегаты, события могут быть многоадресными. Благодаря
использованию множественной адресации на извещение о наступлении события может отвечать несколько объектов.
Пример
using System;
delegate void MyEventHandler();
class MyEvent {
public event MyEventHandler SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null)
SomeEvent();
}
}
Слайд 9class X {
public void XHandler()
{
Console.WriteLine("Событие, получаемое объектом Х");
}
}
class Y {
public void YHandler()
{
Console.WriteLine("Событие, получаемое объектом Y");
}
}
Слайд 10class EventDemo {
static void Handler()
{
Console.WriteLine("Событие, получаемое EventDemo");
}
static void Main(string[] args)
{
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();
}
}
Слайд 11Результат выполнения программы
Событие, получаемое EventDemo
Событие, получаемое объектом X
Событие, получаемое объектом Y
Событие,
получаемое EventDemo
Событие, получаемое объектом Y
Слайд 12
Итак, имя делегата обычно состоит из названия события и слова EventHandler.
Например:
public
delegate ChangedEventHandler(object sender, ChangedEventArgs args)
sender – объект, создавший событие
аrgs – содержащий связанные с событием параметры.
Слайд 13Любое событие – это на самом деле набор двух скрытых методов,
определенных как public. Один из методов начинается с приставки add_, а второй – с приставки remove_.
Например, событие SomeEvent отображается в следующие методы:
//Это событие отображается в скрытые методы
//add_ SomeEvent() и
//remove_ SomeEvent()
public event MyEventHandler SomeEvent;
Помимо скрытых методов add_XXXX() и remove_XXXX() , каждому событию также соответствует статический класс, определенный как private. Его назначение – привязывать событие к соответствующему делегату. При этом при срабатывании события будет вызван каждый из методов делегата. Такой способ позволяет сразу нескольким «приемникам» событий получать одно – единственное происшедшее событие.
Слайд 14Использование аксессоров событий
Существует две формы блока кода event.
Первая форма позволяет создавать
события, которые автоматически управляют списком вызова обработчиков, включая добавление обработчиков в список и удаление обработчиков из списка. Таким образом, не требуется реализовывать функциональность управления этим списком.
Вторая форма блока кода event дает возможность управлять списком обработчика событий программисту. Данная форма позволяет использовать аксессоры событий.
Аксессорная форма блока кода event имеет следующий вид:
event делегат_ события имя_события
{
add
{
// код добавления события в цепочку событий
}
remove
{
// код удаления события из цепочки событий
}
}
Аксессор add вызывается тогда, когда в цепочку добавляется обработчик события с помощью оператора +=, а аксессор remove вызывается, когда из цепочки событий удаляется обработчик события с помощью оператора -=.
Слайд 15Механизм работы с событиями предусматривает несколько этапов.
Объявление делегата – типа, задающего
сигнатуру тех (еще неизвестных на данном этапе) методов, которые будут вызываться при обработке события.(1)
Определение переменной события, имеющей тип делегата события.(2)
Определение генератора события (посылки сообщения), с указанием аргументов, информирующих получателей о состоянии объекта, пославшего сообщение.(3)
Определение методов обработки события. Сигнатура каждого метода должна соответствовать типу делегата события.(4)
Создание экземпляра того делегата, на который “настроено” событие. Аргумент конструктора – имя метода обработки.(5)
Подключение экземпляра делегата к переменной события.(6)
Класс, обрабатывающий события, должен содержать методы обработки или, по крайней мере, иметь доступ к этим методам. В нем реализуются этапы 4, 5, 6.
Второй класс – это класс, генерирующий события, реализует этапы 1, 2, 3.
Зачастую в программе присутствует третий класс, управляющий процессом на более высоком уровне. Его называют супервизором, монитором, диспетчером.
Слайд 16При наличии такого класса и двух подчиненных – класса генерации и
класса обработки событий – схема работы супервизора сводится к следующим шагам.
Создать объект класса генерации событий.
Создать класс обработки событий (может не потребоваться, если метод обработки является статическим).
Создать экземпляр делегата, “настроив” его на метод класса обработки событий.
Подключить экземпляр делегата к переменной события из объекта класса генерации.
Передать управление объекту класса генерации событий( какому – либо из методов, генерирующих события).
Далее все управляется в соответствии с общими принципами событийного управления.
Слайд 17Пример.
Рассмотрим программу с делегатом и четырьмя классами.
Класс Sorting содержит метод,
который сортирует в порядке возрастания одномерный целочисленный массив.
Класс View содержит метод обработки события. Метод выводит на консоль значение счетчика перестановок.
Класс Display визуализирует динамику процесса сортировки – выводит на консоль имитацию элемента управления ProgressBar.
Класс Controller (управляющий). Метод Main() в соответствии с общей схемой создает объект класса, генерирующего события, объект класса – обработчика(View). Затем подключает к переменной события два наблюдателя – два безымянных экземпляра делегата SortHandler.И, наконец, управление передается методу сортировки объекта – генератора событий.
Слайд 18using System;
public delegate void SortHandler(long cn, int si, int ki);
class Sorting
{
int size;
int[] ar;
public long count;
public event SortHandler onSort;
public Sorting(int[] ls)
{
size = ls.Length;
count = 0;
ar = ls;
}
Слайд 19public void sort()
{
int temp;
for(int i = 0;
i {
for(int j = i+1;j < size; j++)
if (ar[i] > ar[j])
{
temp = ar[i];
ar[i] = ar[j];
ar[j] = temp;
count++;
}
if (onSort != null)
onSort(count,size,i);
}
}
}
class View
{
public void nShow(long n, int si, int ki)
{
Console.Write("\t" + n);
}
}
Слайд 20
class Display
{
static int len = 30;
static string
st = null;
public static void barShow(long n, int si, int ki)
{
int pos = Math.Abs((int)((double) ki / si * len));
string s1 = new string('\u258c',pos);
string s2 = new string('-',len - pos - 1) + '\u25c4';
st = s1 + '\u258c' + s2;
Console.Write("\r\t\t" + st);
}
}
class Controller
{
static void Main(string[] args)
{
Random ran = new Random(55);
int [] ar = new int[19999];
for(int i = 0;i < ar.Length;i++)
ar[i] = ran.Next();
Sorting run = new Sorting(ar);
View watch = new View();
run.onSort += new SortHandler(Display.barShow);
run.sort();
Console.Write("\n");
}
}
Слайд 21Событийный делегат SortHandler и переменная события onSort должны быть одинаково доступны
в месте подписки на событие.
При генерации события целесообразно проверять значение переменной события. Эта переменная остается равной null, если на событие нет ни одной подписки.
Обратите внимание на тот факт, что генерация события в отличие от генерации исключения оформляется как обращение к методу. Тем самым после обработки события управление автоматически возвращается в точку, непосредственно следующую за оператором генерации события.
Слайд 22Различные возможности использования событий
События могут быть определены в интерфейсах.
В этом случае
реализующие классы должны обеспечивать реализацию конкретных событий.
События можно определять как абстрактные. Реализацию такого события должен обеспечивать производный класс.
Но, события, объявленные с использованием аксессоров, не могут быть абстрактными.
Событие можно определить как sealed.
Событие может быть виртуальным, т.е. его можно переопределить в производном классе.
Слайд 23Использование анонимных методов с событиями
Анонимный метод можно использовать в качестве обработчика
события, что позволяет обойтись без объявления отдельного метода, а это в свою очередь позволяет значительно сократить код реализации механизма обработки события.
Слайд 24using System;
namespace AnonimDemo
{
delegate void MyEventHandler();
class MyEvent
{
public event MyEventHandler SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent();
}
}
}
class AnonMethHandler
{
public static void Main()
{
MyEvent evt = new MyEvent();
//анонимный метод как обработчик события
evt.SomeEvent += delegate
{
// блок обработки события
Console.WriteLine("Событие получено");
};
evt.OnSomeEvent();
}
}
}
Слайд 25using System;
using System.Collections;
namespace EventDemo
{
public delegate void ChangedEventHandler(object sender,
ChangedEventArgs
EventArgs);
public class ChangedEventArgs:EventArgs
{
private object item;
private bool permit;
public object Item
{
get {return (item);}
set {item = value;}
}
public bool Permit
{
get { return(permit);}
set { permit = value;}
}
Слайд 26public class ListWithChangedEvent:ArrayList
{
public event ChangedEventHandler
Changed;
private ChangedEventArgs evargs = new ChangedEventArgs();
protected virtual void OnChanged(ChangedEventArgs args)
{
if (Changed != null)
Changed(this, args);
}
public override int Add(object value)
{
int i = 0;
evargs.Item = value;
OnChanged(evargs);
if (evargs.Permit) i = base.Add(value);
else Console.WriteLine(" добавление элемента запрещено" +
"Значение = {0}", value);
return i;
}
Слайд 27public override void Clear()
{
evargs.Item = 0;
OnChanged(evargs);
base.Clear();
}
public override object this[int index]
{
set{
evargs.Item = value;
OnChanged(evargs);
if (evargs.Permit)
base[index] = value;
else Console.WriteLine
( "Замена элемента запрещена"
+ "Значение = {0}", value);
}
get{return (base[index]);}
}
Слайд 28public class EventReceiver1
{
private ListWithChangedEvent List;
public EventReceiver1(ListWithChangedEvent list)
{
List = list;
OnConnect();
}
private void ListChanged(object sender, ChangedEventArgs
args)
{
Console.WriteLine(
" EventReceiverl: сообщаю об изменениях:"
+ "Item = {0}", args.Item);
args.Permit = ((int)args.Item < 10);
}
public void OnConnect()
{
List.Changed += new ChangedEventHandler
(ListChanged);
}
}
Слайд 29class EventReceiver2
{
private ListWithChangedEvent List;
public EventReceiver2(ListWithChangedEvent list)
{
List = list;
OnConnect();
}
private void ListChanged(object sender,
ChangedEventArgs args)
{
Console.WriteLine("Receiver2:сообщаю об изменениях:"
+ "объект класса {0}: Item = {1}",
sender.GetType(), args.Item);
args.Permit = ((int)args.Item < 20);
}
public void OnConnect()
{
List.Changed += new ChangedEventHandler(ListChanged);
}
}
Слайд 30class Class1
{
static void Main()
{
ListWithChangedEvent list = new ListWithChangedEvent();
ListWithChangedEvent list1 = new ListWithChangedEvent();
EventReceiver1 Receiver1 = new EventReceiver1(list);
EventReceiver2 Receiver21 = new EventReceiver2(list);
EventReceiver2 Receiver22 = new EventReceiver2(list);
Random rnd = new Random();
list.Add(rnd.Next(20));
list.Add(rnd.Next(20));
list[1] = 17;
int val = (int)list[0] + (int)list[1];
list.Add(val);
list.Clear();
list1.Add(10);
list1[0] = 25;
list1.Clear();
list.Add(21);
list.Clear();
Console.ReadLine();
}
}
}
}