Выбрать главу

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.

Наследование и универсальность

Необходимость в универсализации возникает с первых шагов программирования. Одна из первых процедур, появляющихся при обучении программированию — это процедура свопинга: обмен значениями двух переменных одного типа. Выглядит она примерно так: