Рейтинговые книги
Читем онлайн ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 71 72 73 74 75 76 77 78 79 ... 259

public class Car {

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

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

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

 …

}

Теперь вызывающая сторона может зарегистрировать несколько целевых объектов.

class Program {

 static void Main(string[] args) {

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

  // Регистрация множества обработчиков событий.

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

  c1.OnAboutToBlow(new Car.AboutToBlow(CarlsAlmostDoomed));

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

  …

 }

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

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

 public static void CarIsAlmostDoomed(string msg) {Console.WriteLine("Важное сообщение от Car: {0}", msg);}

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

}

В программном воде CIL операция += преобразуется в вызов статического метода Delegate.Combine() (можно было бы вызвать Delegate.Combine() непосредственно, но операция += предлагает более простую альтернативу). Взгляните, например, на CIL-представление метода OnAboutToBlow().

.method public hidebysig instance void OnAboutToBlow (class CarDelegate.Car/AboutToBlow clientMethod) cil managed {

 .maxstack 8

 ldarg.0

 dup

 ldfld class CarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList

 ldarg.1

 call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)

 castclass CarDelegate.Car/AboutToBlow

 stfld class СarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList

 ret

}

Класс Delegate определяет также статический метод Remove(), который позволит вызывающей стороне динамически удалять элементы из списка вызовов. Легко догадаться, что в C# разработчики могут для этого использовать перегруженную операцию -=. Чтобы предоставить вызывающей стороне возможность не привязываться к обозначениям AboutToBlow и Exploded, можно добавить в тип Car следующие вспомогательные методы (обратите внимание на операцию -=).

public class Car {

 // Удаление элемента из списка вызовов.

 public void RemoveAboutToBlow(AboutToBlow clientMethod) {almostDeadList -= clientMethod;}

 public void RemoveExploded(Exploded clientMethod) {explodedList -= clientMethod;}

 ...

}

Здесь синтаксис -= тоже выступает в качестве простого сокращения для вызова статического метода Delegate.Remove(), что доказывается следующим программным кодом CIL для члена RemoveAboutToBlow() типа Car.

.method public hidebysig instance void RemoveAboutToBlow(class CarDelegate.Car/AboutToBlow clientMethod) cil managed {

 .maxstack 8

 ldarg.0

 dup

 ldfld class CarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList

 ldarg.1

 call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)

 castclass CarDelegate.Car/AboutToBlow

 stfld class CarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList

 ret

}

Если вызывающая сторона пожелает удалить элемент из списка вызовов делегата, мы должны предоставить тот же объект делегата, который был ранее добавлен. Поэтому мы можем прекратить выдачу сообщения Exploded, обновив Main() так, как показано ниже.

static void Main(string[] args) {

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

 // Сохранение объекта Car.Exploded делегата.

 Car.Exploded d = new Car.Exploded(CarExploded);

 cl.OnExploded(d);

 …

 // Удаление метода CarExploded из списка вызовов.

 cl.RemoveExploded(d);

 …

}

Вывод нашего приложения CarDelegate показан на рис. 8.5.

Рис. 8.5. Приложение CarDelegate за работой

Исходный код. Проект CarDelegate размещен в подкаталоге, соответствующем главе 8.

Более совершенный пример делегата

Чтобы продемонстрировать более интересные варианты использования делегатов, мы добавим в класс Car пару членов-переменных логического типа. Первый из добавляемых членов предназначен для индикации того, что автомобиль необходимо помыть (isDirty), а второй будет сообщать о том, что автомобиль требует замены шин (shouldRotate). Чтобы позволить пользователю объекта взаимодействовать с этими новыми данными состояния, для Car определяются необходимые дополнительные свойства и обновляется конструктор. Вот как выглядит соответствующим образом модифицированный программный код.

// Обновленный класс Car.

public class Car {

 …

 // Не пора ли помыть? Не пора ли сменить шины?

 private bool isDirtу;

 private bool shouldRotate;

 // Дополнительные параметры для установки булевых значений.

 public Car(string name, int maxSp, int currSp, bool washCar, bool rotateTires) {

  …

  isDirty = washCar;

  shouldRotate = rotateTires;

 }

 public bool Dirty {

  get { return isDirty; }

  set { isDirty = value; }

 }

 public bool Rotate {

  get { return shouldRotate; }

  set { shouldRotate = value; }

 }

}

Также предположим, что в тип Car вложен новый делегат CarDelegate.

// Для Car определяется еще один делегат.

public class Car {

 …

 // Может вызывать любой метод, получающий Car в виде параметра

 // и не возвращающий ничего.

 public delegate void CarDelegate(Car с);

 …

}

Здесь создается делегат с именем CarDelegate. Тип CarDelegate представляет "некоторую функцию", принимающую Car в качестве параметра и возвращающую пустое значение.

Делегаты в качестве параметров

Теперь, когда у нас есть новый тип делегата, который указывает на методы, получающие Car в виде параметра и не возвращающие ничего, мы можем создавать функции, которые принимают этот делегат в виде параметра. Для примера предположим, что у нас есть новый класс, которому назначено имя Garage (гараж). Этот тип поддерживает коллекцию типов Car, содержащихся в System.Collections. ArrayList. При создании ArrayList наполняется типами Car.

// Класс Garage хранит список типов Car.

using System.Collections;

 …

 public class Garage {

 // Создание списка всех машин в гараже.

 ArrayList theCars = new ArrayList();

 // Создание машин в гараже.

 public Garage() {

  // Напомним, что конструктор был обновлен,

  // и теперь можно установить значения isDirty и shouldRotate.

  theCars.Add(new Car("Viper", 100, 0, true, false));

  theCars.Add(new Car("Fred", 100, 0, false, false));

  theCars.Add(new Car("BillyBob", 100, 0, false, true));

 }

}

Класс Garage будет определять общедоступный метод ProcessCars(), который в качестве единственного аргумента получит новый тип делегата (Car.CarDelegate). В ProcessCars() каждый объект Car из коллекции будет передаваться в виде параметра "той функции, на которую указывает" делегат. При этом ProcessCars() использует члены Target и Method из System.MulticastDelegate, чтобы определить, на какую из функций делегат указывает в настоящий момент.

// Класс Garage имеет метод, использующий CarDelegate.

using System.Collections;

public class Garage {

 …

 // Этот метод получает Car.CarDelegate в виде параметра.

 public void ProcessCars(Car.CarDelegate proc) {

  // Куда направить вызов? 

  Console.WriteLine("***** Вызывается: {0} *****", proc.Method);

  // Вызывается метод экземпляра или статический метод?

  if (proc.Target != null) Console.WriteLine("-›Цель: {0} ", proc.Target);

  else Console.WriteLine("-›Целевым является статический метод");

  // Вызов "указанного" метода всех машин по очереди.

  foreach (Car с in theCars) {

   Console.WriteLine("n-› Обработка Car");

   proc(c);

  }

 }

}

Как и в случае любого делегата, при вызове ProcessCars() мы должны указать имя метода, который обработает запрос. Напомним, что такой метод может быть или статическим, или методом экземпляра. Для примера предположим, что в качестве такого метода будут использоваться члены экземпляра нового класса ServiceDepartment (отдел технического обслуживании), которым назначены имела WashCar() и RotateTires(). Обратите внимание на то, что эти два метода используют новые свойства Rotate и Dirty типа Car.

// Этот класс определяет методы, которые будут вызываться

// типом Car.CarDelegate.

public class ServiceDepartment {

 public void WashCar(Car c) {

  if (c.Dirty) Console.WriteLine("Моем машину");

  else Console.WriteLine("Эта машина уже помыта…");

1 ... 71 72 73 74 75 76 77 78 79 ... 259
На этой странице вы можете бесплатно читать книгу ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен бесплатно.
Похожие на ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен книги

Оставить комментарий