public abstract class Receiver
{
private NewTown town;
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);
town = null;
}
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)
{
Console.WriteLine("Пожар в доме {0}. День {1}-й. "
+ " Милиция ищет виновных!", е. Build,е. Day);
е. Permit &= true;
}
}// class Police
public class FireDetect: Receiver
{
public FireDetect (NewTown town): base(town){}
public override void It_is_Fire(object sender, FireEventArgs e)
{
Console.WriteLine("Пожар в доме {0}. День {1}-й."+
" Пожарные тушат пожар!", е. Build,е. Day);
Random rnd = new Random(e.Build);
if(rnd.Next(10) >5)
e. Permit &= false;
else e.Permit &=true;
}
}// class FireDetect public class Ambulance: Receiver
{
public Ambulance(NewTown town): base(town){}
public override void It_is_Fire(object sender,
FireEventArgs e)
{
Console.WriteLine("Пожар в доме {0}. День {1}-й."+
" Скорая спасает пострадавших!", е. Build,е. Day);
е. Permit &= true;
}
}// class Ambulance
Для каждого потомка задан конструктор, вызывающий базовый метод родителя. Каждый потомок по-своему определяет обработчика события Fire. Обратите внимание на то, как в данном проекте решается проблема с выходным параметром события — Permit. Принята следующая стратегия: возвращаемое значение Permit будет истинно, если все обработчики согласны с этим. Поэтому каждый обработчик использует конъюнкцию выработанного им значения со значением, пришедшим от предыдущего обработчика. В данном примере все зависит от пожарных, которые могут вырабатывать разные решения.
Для полноты картины необходимо показать, как выглядит класс, задающий аргументы события, который, как и положено, является потомком класса EventArgs;
public class FireEventArgs: EventArgs
{
private int build;
private int day;
private bool permit;
public int Build
{
get{ return(build);} ///set{ build = value;}
}
public int Day
{
get{ return(day);} ///set{ day = value;}
}
public bool Permit
{
get{ return(permit);} set{ permit = value;}
}
public FireEventArgs(int build, int day, bool permit)
{
this.build = build; this.day = day; this.permit = permit;
}
}//class FireEventArgs
Входные параметры события — build и day защищены от обработчиков события, а корректность выходного параметра гарантируется тщательным программированием самих обработчиков.
Для завершения проекта нам осталось определить тестирующую процедуру в классе Testing, создающую объекты и запускающую моделирование жизни города:
public void TestLifeTown()
{
NewTown sometown = new NewTown(100,100);
sometown.LifeOurTown ();
}
Результаты ее работы зависят от случайностей. Вот как выглядит один из экспериментов:
Рис. 21.3. События в жизни города
22. Универсальность. Классы с родовыми параметрами
Наследование и универсальность — взаимно дополняющие базовые механизмы создания семейства классов. Родовые параметры универсального класса. Синтаксис универсального класса. Родовое порождение экземпляров универсального класса. Методы с родовыми параметрами. Ограниченная универсальность — ограничения, накладываемые на родовые параметры. Виды ограничений. Ограничение универсальности — это свобода действий. Примеры. Родовые параметры и частные случаи классов: структуры, интерфейсы, делегаты. Универсальность и Framework.Net.
Наследование и универсальность
Необходимость в универсализации возникает с первых шагов программирования. Одна из первых процедур, появляющихся при обучении программированию — это процедура свопинга: обмен значениями двух переменных одного типа. Выглядит она примерно так: