Шрифт:
Теперь вы должны увидеть вывод, показанный на рис. 8.4.
Рис. 8.4. Проверка списка вызовов делегата (новая попытка)
Исходный код. Проект SimpleDelegate размещен в подкаталоге, соответствующем главе 8.
Модификация типа Car с учетом делегатов
Очевидно, что предыдущий пример SimpleDelegate был исключительно иллюстративным, поскольку нет никаких реальных причин строить делегаты для простого сложения двух чисел. Но этот пример раскрывает принципы работы с типами делегата. Для построения более реального примера мы модифицируем тип Car так, чтобы он посылал сообщения Exploded и AboutToBlow через делегаты .NET, a не через пользовательский интерфейс обратного вызова. Кроме отказа от реализации IEngineEvents, мы должны выполнить следующие шаги:
• определить делегаты AboutToBlow и Exploded;
• объявить члены-переменные всех типов делегата в классе Car;
• создать вспомогательные функции Car, которые позволят вызывающей стороне указать методы, поддерживаемые членами-переменными делегатов;
• обновить метод Accelerate, чтобы иметь возможность в подходящей ситуации обратиться к списку вызовов делегата.
Рассмотрите следующий обновленный класс Car, в котором решены первые три из указанных задач.
Обратите внимание на то, что в этом примере мы определяем типы делегата непосредственно в рамках типа Car. Если исследовать библиотеки базовых классов, то станет ясно, что определение делегата в рамках типа, с которым он обычно работает, является вполне типичным. В связи с этим, поскольку компилятор преобразует делегат в полное определение класса, мы здесь фактически создаем вложенные классы.
Далее обратите внимание на то, что здесь объявлены члены-переменные (по одному для каждого типа делегата) и вспомогательные функции (OnAboutToBlow и OnExploded), которые позволят клиенту добавлять методы в списки вызовов делегатов. В принципе эти методы подобны методам Advise и Unadvise, которые были нами созданы в примере с EventInterfасе. Но в данном случае входящим параметром оказывается размещаемый клиентом объект делегата, а не класс, реализующий конкретный интерфейс.
Здесь мы должны обновить метод Accelerate, чтобы вызывались делегаты, а не просматривались объекты ArrayList приемников клиента (как это было в примере с EventInterfасе). Подходящая модификация может выглядеть так.
Обратите внимание на то, что перед вызовом методов, связанных с членами-переменными almostDeadList и explodedList, их значения проверяются на допустимость. Причина в том, что размещение соответствующих объектов с помощью вызова вспомогательных методов OnAboutToBlow и OnExploded будет задачей вызывающей стороны. Если вызывающая сторона не вызовет эти методы, а мы попытаемся получить список вызовов делегата, то будет сгенерировано исключение NullReferenseException и в среде выполнения возникнут проблемы (что, конечно же, нежелательно).
Теперь, когда инфраструктура делегата имеет нужный нам вид, рассмотрим модификацию класса Program.