По законам классического наследования в идеале лучше иметь один тип делегата, который мог бы указывать на методы, возвращающие либо тип Car, либо тип SportsCar (в конце концов, тип SportsCar связан с Car отношением наследования).
class Program {
// Определение делегата, способного возвращать
// как Car, так и SportsCar.
public delegate Car ObtainVehicalDelegate();
public static Car GetBasicCar() {return new Car();}
public static SportsCar GetSportsCar() { return new SportsCar();}
static void Main(string[] args) {
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;
…
}