Console.WriteLine("***** Ковариантность делегатов *****\n");
ObtainVehicalDelegate targetA = new ObtainVehicalDelegate(GetBasicCar);
Car c = targetA;
// Такое присваивание возможно вследствие ковариантности.
ObtainVehicalDelegate targetB = new ObtainVehicalDelegate(GetSportsCar);
SportsCar sc = (SportsCar)targetB;
Console.ReadLine;
}
}
Обратите внимание на то, что тип делегата ObtainVehicalDelegate был определен для того, чтобы указывать на методы, возвращающие строго типизованный Car. Однако в условиях ковариантности мы получаем возможность указывать и на методы, возвращающие производные типы. Чтобы получить производный тип, нужно просто выполнить явное преобразование.
Замечание.
Точно так жеИсходный код.
Проект DelegateCovariance размещен в подкаталоге, соответствующем главе 8.События в C#
Делегаты оказываются очень интересными конструкциями с той точки зрения, что они предоставляют возможность реализовать двухстороннее взаимодействие между объектами в памяти. Однако, и вы с этим согласитесь, работа с делегата-ми напрямую предполагает ввод больших по объему шаблонных фрагментов программного кода (определение делегата, объявление членов-переменных, создание пользовательских методов регистрации и отмены регистрации).
Поскольку возможность обратного вызови объектов другим объектом является очень полезной, в C# предлагается специальное ключевое слово event, позволяющее минимизировать неудобства программиста, связанные с непосредственным применением делегатов. При обработке ключевого слова event компилятор автоматически создает для вас методы регистрации и отмены регистрации, а также члены-переменные, необходимые для вашего типа делегата. Ключевое слово event Можно назвать синтаксической "конфеткой", позволяющей экономить время при вводе программного кода.
Замечание.
Даже при использовании в C# ключевого слова event вам все равно придется вручную определять связанные с делегатом типы.Процесс определения события состоит из двух шагов. Во-первых, вы должны определить делегат, который будет содержать методы, вызываемые при наступлении соответствующего события. Затем вы объявляете события (используя ключевое слово C# event) в терминах соответствующего делегата. Определение типа, способного посылать события, имеет следующий шаблон (записанный здесь в псевдокоде).
public class SenderOfEvents {
public
public event
…
}
События типа Car будут иметь те же имена, что и предыдущие делегаты (AboutToBlow и Exploded). Новому делегату, с которым будут ассоциироваться события, будет назначено имя CarEventHandler. Вот начальные изменения, вносимые в определение типа Car.
public class Car {
// Этот делегат работает в связке с событиями Car
public delegate void CarEventHandler(string msg);
// Объект Car может посылать эти события.
public event CarEventHandler Exploded;
public event CarEventHandler AboutToBlow;
…
}
Отправка событий вызывающей стороне выполняется с помощью простого указания имени события и всех обязательных параметров, предусмотренных в определении соответствующего делегата. Вы должны проверить событие на значение null перед тем, как вызывать набор методов делегата, чтобы гарантировать регистрацию события вызывающей стороной. С учетом этого предлагается новый вариант метода Accelerate для типа Car.
public void Accelerate(int delta) {
// Если машина сломана, генерируется событие Exploded.
if (carIsDead) {
if (Exploded!= null) Exploded("Извините, машина сломалась…");
} else {
currSpeed += delta;
// Вот-вот сломается?
if (10 == maxSpeed – currSpeed && AboutToBlow != null) {
AboutToBlow ("Осторожно! Могу сломаться!");
}
// Пока все OK!