Здесь следует отметить только то, вызывающая сторона задает значения членам-переменным делегата с помощью вспомогательных методов регистрации. Кроме того, поскольку делегаты AboutToBlow и Exploded вложены в класс Car, при их размещении следует использовать полные имена (например, Car.AboutToBlow). Как любому конструктору, мы передаем конструктору делегата имя метода, который нужно добавить в список вызовов. В данном случае это два статических члена класса Program (если вложить указанные методы в новый класс, это будет очень похоже на тип CarEventSink из примера Event Interface).
Реализация групповых вызовов
Напомним, что делегаты .NET наделены возможностью
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
.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;}
...
}