public int Val { get; set; }
public Alpha(int v) { Val = v; }
}
class Beta : Alpha {
public Beta(int v) : base(v) { }
}
class GenDelegateVarianceDemo {
// Возвратить логическое значение true, если значение
// переменной obj.Val окажется четным,
static bool IsEven(Alpha obj) {
if ((obj.Val % 2) == 0) return true;
return false;
}
static Beta ChangeIt(Alpha obj) {
return new Beta(obj.Val + 2);
}
static void Main() {
Alpha objA = new Alpha(4);
Beta objB = new Beta(9);
// Продемонстрировать сначала контравариантность.
// Объявить делегат SomeOp
SomeOp
// Объявить делегат SomeOp
SomeOp
//А теперь- присвоить делегат SomeOp
// *** Это допустимо только благодаря контравариантности. ***
checklt2 = checklt;
// Вызвать метод через делегат.
Console.WriteLine(checklt2(objB));
// Далее, продемонстрировать контравариантность.
// Объявить сначала два делегата типа AnotherOp.
// Здесь возвращаемым типом является класс Beta,
//а параметром типа — класс Alpha.
// Обратите внимание на то, что для делегата modifylt
// задается метод Changelt.
AnotherOp
// Здесь возвращаемым типом является класс Alpha,
// а параметром типа — тот же класс Alpha.
AnotherOp
// А теперь присвоить делегат modifylt делегату modifyIt2.
// *** Это допустимо только благодаря ковариантности. ***
modifyIt2 = modifyIt;
// Вызвать метод и вывести результаты на экран.
objA = modifyIt2(objA);
Console.WriteLine(objA.Val);
}
}
Выполнение этой программы приводит к следующему результату.
False
6
Каждая операция достаточно подробно поясняется в комментариях к данной программе. Следует особо подчеркнуть, для успешной компиляции программы в объявлении обоих типов делегатов SomeOp
and AnotherOp
должны быть непременно указаны ключевые слова in
и out
соответственно. Без этих модификаторов компиляция программы будет выполнена с ошибками из-за отсутствия неявных преобразований типов в означенных строках кода.
Создание экземпляров объектов обобщенных типов
Когда приходится иметь дело с обобщениями, то нередко возникает вопрос: не приведет ли применение обобщенного класса к неоправданному раздуванию кода? Ответ на этот вопрос прост: не приведет. Дело в том, что в C# обобщения реализованы весьма эффективным образом: новые объекты конструируемого типа создаются лишь по мере надобности. Этот процесс описывается ниже.
Когда обобщенный класс компилируется в псевдокод MSIL, он сохраняет все свои параметры типа в их обобщенной форме. А когда конкретный экземпляр класса потребуется во время выполнения программы, то JIT-компилятор сконструирует конкретный вариант этого класса в исполняемом коде, в котором параметры типа заменяются аргументами типа. В каждом экземпляре с теми же самыми аргументами типа будет использоваться один и тот же вариант данного класса в исполняемом коде.
Так, если имеется некоторый обобщенный класс Gen
, то во всех объектах типа Gen
будет использоваться один и тот же исполняемый код данного класса. Следовательно, раздувание кода исключается благодаря тому, что в программе создаются только те варианты класса, которые действительно требуются. Когда же возникает потребность сконструировать объект другого типа, то компилируется новый вариант класса в исполняемом коде.