Здравствуйте! Записался на ваш курс, но не понимаю как произвести оплату. Надо ли писать заявление и, если да, то куда отправлять? как я получу диплом о профессиональной переподготовке? |
Классы с событиями
Проект "Город и его службы"
Завершить лекцию хочется проектом, в котором собраны все рассматриваемые аспекты работы с событиями. Вернемся к моделированию жизни города, происходящих в нем событий, реакции на них городских служб. Наша главная цель в данном проекте еще раз показать, как возникающее событие, в данном случае - пожар в одном из домов города, обрабатывается по-разному городскими службами - пожарными, милицией, скорой помощью. В нашей модели сообщение о событии получает не одна, как ранее, а несколько служб. Событие имеет как входные, так и выходные аргументы, так что городу необходимо будет анализировать результаты работы каждой службы. Стоит обратить внимание и на организацию взаимодействия между классами, задающего город и его службы. Конечно, все упрощено, в реальном городе событиями являются не только пожары и преступления, но и более приятные ситуации: день города, открытие фестивалей и выставок, строительство новых театров и институтов.
Начнем с описания делегата, задающего событие "пожар" и построенного по всем правилам.
public delegate void FireEventHandler(object sender, FireEventArgs e);
А теперь рассмотрим свойства класса, определяющего новый город.
/// <summary> /// Модель города с событиями /// и следящих за ними службами города /// </summary> public class NewTown { //свойства string townName; //название города int buildings; //число домов в городе int days; //число дней наблюдения //городские службы Police policeman; Ambulance ambulanceman; FireDetect fireman; //события в городе public event FireEventHandler Fire; string[] resultService; //результаты действий служб //моделирование случайных событий private Random rnd = new Random(); //вероятность пожара в доме в текущий день double fireProbability;
В нашем городе есть дома; есть время, текущее день за днем; городские службы; событие "пожар", которое, к сожалению, может возникать с заданной вероятностью каждый день в каждом доме. Рассмотрим конструктор объектов нашего класса:
/// <summary> /// Конструктор города /// Создает службы и включает наблюдения /// за событиями /// </summary> /// <param name="name">название города</param> /// <param name="buildings">число домов</param> /// <param name="days">число дней наблюдения</param> public NewTown(string name, int buildings, int days) { townName = name; this.buildings = buildings; this.days = days; fireProbability = 1e-3; //Создание служб policeman = new Police(this); ambulanceman= new Ambulance(this); fireman= new FireDetect(this); //Подключение к наблюдению за событиями policeman.On(); ambulanceman.On(); fireman.On(); }
Конструктору передается имя города, число домов в нем и период времени, в течение которого будет моделироваться жизнь города. Конструктор создает службы города - объекты соответствующих классов Police, Ambulance, FireDetect, передавая им ссылку на сам объект "город". После создания служб вызываются их методы On, подключающие обработчики события Fire каждой из этих служб к событию.
А теперь самое главное - определим метод OnFire, зажигающий события, с учетом того, что несколько служб слушают событие и у события есть выходные аргументы, задающие результаты работы каждой службы.
/// <summary> /// Зажигается событие. /// Поочередно вызываются обработчики события /// </summary> /// <param name="e"> /// входные и выходные аргументы события /// </param> protected virtual void OnFire(FireEventArgs e) { const string MESSAGE_FIRE = "В городе {0} пожар! Дом {1}. День {2}-й"; Console.WriteLine(string.Format(MESSAGE_FIRE, townName, e.Building, e.Day)); if (Fire != null) { Delegate[] eventHandlers = Fire.GetInvocationList(); resultService = new string[eventHandlers.Length]; int k = 0; foreach (FireEventHandler evHandler in eventHandlers) { evHandler(this, e); resultService[k++] = e.Result; } } }
Обратите внимание: метод GetInvocatonList возвращает массив объектов класса Delegate, который является абстрактным классом и родителем для классов событий, в частности для класса FireEventHandler. Получив этот массив, далее с ним можно работать привычным образом. В цикле по элементам массива вызывается очередной обработчик события, результаты его работы сохраняются в специально созданном массиве resultService.
Где и когда будет включаться событие Fire? Напишем метод, моделирующий жизнь города, где для каждого дома каждый день будет проверяться, а не возник ли пожар, и, если это случится, будет включено событие Fire:
/// <summary> /// Моделирование жизни города /// </summary> public void LifeOurTown() { const string OK = "В городе {0} все спокойно! Пожаров не было."; bool wasFire = false; for(int day = 1; day <= days; day++) for(int building = 1; building <= buildings; building++) { if (rnd.NextDouble() < fireProbability) { FireEventArgs e = new FireEventArgs(building, day); OnFire(e); wasFire = true; for (int i = 0; i < resultService.Length; i++) Console.WriteLine(resultService[i]); } } if (!wasFire) Console.WriteLine(string.Format(OK, townName)); }
Рассмотрим теперь классы receiver, обрабатывающие событие Fire. Их у нас три, по одному на каждую городскую службу. Все три класса устроены по одному образцу. Напомню: каждый такой разумно устроенный класс, кроме обработчика события, имеет конструктор, инициализирующий ссылку на объект, создающий события, методы подключения и отсоединения обработчика от события. В такой ситуации целесообразно построить вначале абстрактный класс Receiver, в котором будет предусмотрен обработчик события, но не задана его реализация, а затем для каждой службы построить класс потомок. Начнем с описания родительского класса:
public abstract class Receiver { protected NewTown town; protected Random rnd = new Random(); public Receiver(NewTown town) {this.town = town;} public void On() { town.Fire += new FireEventHandler(It_is_Fire); } public void Off() { town.Fire -= new FireEventHandler(It_is_Fire); } public abstract void It_is_Fire(object sender, FireEventArgs e); }//class Receiver
Каждый из классов потомков устроен одинаково - имеет конструктор и задает реализацию абстрактного метода It_is_Fire. Вот описания этих классов:
public class Police : Receiver { public Police (NewTown town): base(town){} public override void It_is_Fire(object sender, FireEventArgs e) { const string OK = "Милиция нашла виновных!"; const string NOK = "Милиция не нашла виновных! Следствие продолжается."; if (rnd.Next(0, 10) > 6) e.Result = OK; else e.Result = NOK; } }// class Police public class FireDetect : Receiver { public FireDetect (NewTown town): base(town){} public override void It_is_Fire(object sender, FireEventArgs e) { const string OK = "Пожарные потушили пожар!"; const string NOK = "Пожар продолжается! Требуется помощь."; if (rnd.Next(0, 10) > 4) e.Result = OK; else e.Result = NOK; } }// class FireDetect public class Ambulance : Receiver { public Ambulance(NewTown town): base(town){} public override void It_is_Fire(object sender, FireEventArgs e) { const string OK = "Скорая оказала помощь!"; const string NOK = "Есть пострадавшие! Требуются лекарства."; if (rnd.Next(0, 10) > 2) e.Result = OK; else e.Result = NOK; } }// class Ambulance
Для полноты картины необходимо показать, как выглядит класс, задающий аргументы события, который, как и положено, является потомком класса EventArgs:
/// <summary> /// Класс,задающий входные и выходные аргументы события /// </summary> public class FireEventArgs : EventArgs { int building; int day; string result; //Доступ к входным и выходным аргументам public int Building { get{return building;} } public int Day { get{return day;} } public string Result { get { return result; } set{result = value;} } public FireEventArgs(int building, int day) { this.building = building; this.day = day; } }//class FireEventArgs
Входные аргументы события - build и day защищены от обработчиков события, а корректность работы с выходным аргументом гарантируется аккуратным программированием вызова обработчиков.
Для завершения проекта нам осталось определить тестирующую процедуру в классе Testing, создающую объекты и запускающую моделирование жизни города:
public void TestLifeTown() { NewTown sometown = new NewTown("Канск", 20, 100); sometown.LifeOurTown(); }
Результаты ее работы зависят от случайных событий. Вот как выглядит один из экспериментов.
Проекты
- Создайте проект "Жизнь города", в котором происходят разные события.
- Создайте проект "Жизнь факультета" с событием "День факультета".
- Создайте проект "Жизнь факультета", в котором происходят разные события.
- Создайте проект "Жизнь лошади".
- Создайте проект "Жизнь автомобиля".
- Создайте проект "Жизнь парохода".
- Создайте проект "Жизнь студента".
- Создайте проект "Работа конвейера".
- Создайте проект "Военные действия". Классы, задающие противников, взаимно обрабатывают события друг друга. Например, объект одного класса зажигает событие "атака", обработчик этого события в другом классе в ответ зажигает событие "контратака".
- Создайте проект "Быки и Медведи", где объекты класса "Быки" играют на бирже на повышение, а "Медведи" - на понижение.
- Исследуйте возможность создания класса, в котором одни объекты этого класса создают события, а другие объекты этого же класса обрабатывают эти события.