// Добавление элементов в список вызовов

 // с помощью вспомогательных методов.

 public void OnAboutToBlow(AboutToBlow clientMethod) {almostDeadList = clientMethod;}

 public void OnExploded(Exploded clientMethod) {explodedList = clientMethod;}

 …

}

Обратите внимание на то, что в этом примере мы определяем типы делегата непосредственно в рамках типа Car. Если исследовать библиотеки базовых классов, то станет ясно, что определение делегата в рамках типа, с которым он обычно работает, является вполне типичным. В связи с этим, поскольку компилятор преобразует делегат в полное определение класса, мы здесь фактически создаем вложенные классы.

Далее обратите внимание на то, что здесь объявлены члены-переменные (по одному для каждого типа делегата) и вспомогательные функции (OnAboutToBlow и OnExploded), которые позволят клиенту добавлять методы в списки вызовов делегатов. В принципе эти методы подобны методам Advise и Unadvise, которые были нами созданы в примере с EventInterfасе. Но в данном случае входящим параметром оказывается размещаемый клиентом объект делегата, а не класс, реализующий конкретный интерфейс.

Здесь мы должны обновить метод Accelerate, чтобы вызывались делегаты, а не просматривались объекты ArrayList приемников клиента (как это было в примере с EventInterfасе). Подходящая модификация может выглядеть так.

public void Accelerate(int delta) {

 // Если машина 'сломалась', генерируется событие Exploded.

 if (carIsDead) {

  if (explodedList != null) explodedList("Извините, машина сломалась…");

 } elsе {

  currSpeed += delta;

  // Вот-вот сломается?

  if (10 == maxSpeed – currSpeed && almostDeadList != null) {

   almostDeadList("Осторожно! Могу сломаться!");

  }

  // Пока все OK!

  if (currSpeed ›= maxSpeed) carIsDead = true;

  else Console.WriteLine("CurrSpeed = {0}", currSpeed);

 }

}

Обратите внимание на то, что перед вызовом методов, связанных с членами-переменными almostDeadList и explodedList, их значения проверяются на допустимость. Причина в том, что размещение соответствующих объектов с помощью вызова вспомогательных методов OnAboutToBlow и OnExploded будет задачей вызывающей стороны. Если вызывающая сторона не вызовет эти методы, а мы попытаемся получить список вызовов делегата, то будет сгенерировано исключение NullReferenseException и в среде выполнения возникнут проблемы (что, конечно же, нежелательно).

Теперь, когда инфраструктура делегата имеет нужный нам вид, рассмотрим модификацию класса Program.

class Program {

 static void Main(string[] args) {

  Console.WriteLine("***** Делегаты и контроль событий *****");

  // Обычное создание класса Car.

  Car cl = new Car("SlugBug", 100, 10);

  // Регистрация обработчиков событий для типа Car.

  cl.OnAboutToBlow(new Car.AboutToBlow(CarAboutToBlow));

  cl.OnExploded(new Car.Exploded(CarExploded));

  // Ускоряемся (при этом генерируются события) .

  Console.WriteLine("\n***** Ускорение *****");

  for(int i = 0; i ‹ 6; i++) cl.Accelerate(20);

  Console.ReadLine;

 }

 // Car будет вызывать эти методы.

 public static void CarAboutToBlow(string msg) {Console.WriteLine(msg);}

 public static void CarExploded(string msg) {Console.WriteLine(msg);}

}

Здесь следует отметить только то, вызывающая сторона задает значения членам-переменным делегата с помощью вспомогательных методов регистрации. Кроме того, поскольку делегаты AboutToBlow и Exploded вложены в класс Car, при их размещении следует использовать полные имена (например, Car.AboutToBlow). Как любому конструктору, мы передаем конструктору делегата имя метода, который нужно добавить в список вызовов. В данном случае это два статических члена класса Program (если вложить указанные методы в новый класс, это будет очень похоже на тип CarEventSink из примера Event Interface).

Перейти на страницу:

Похожие книги