Далее создается переменная ссылочного типа IMyContraVarGenIF, но на этот раз ей присваивается ссылка на объект класса MyClass. Эта операция вполне допустима, поскольку обобщенный тип Т объявлен как контравариантный.
Как и следовало ожидать, следующая строка, в которой вызывается метод BetaRef. Show () с аргументом Beta, является вполне допустимой. Ведь Beta — это обобщенный тип Т в классе MyClass и в то же время аргумент в методе Show ().
В следующей строке переменная AlphaRef присваивается переменной BetaRef. Эта операция вполне допустима лишь в силу контравариантности. В данном случае переменная относится к типу MyClass, а переменная AlphaRef — к типу MyClass. Но поскольку Alpha является базовым классом для класса Beta, то такое преобразование типов оказывается допустимым благодаря контравариантности. Для того чтобы убедиться в необходимости контравариантности в рассматриваемом здесь примере, попробуйте удалить ключевое слово in из объявления обобщенного типа Т в интерфейсе IMyContraVarGenlF, а затем попытайтесь скомпилировать приведенный выше код еще раз. В результате появятся ошибки компиляции.
Ради большей наглядности примера вся рассмотренная выше последовательность операций собрана ниже в единую программу.
// Продемонстрировать контравариантность в обобщенном интерфейсе, using System;
// Это обобщенный интерфейс, поддерживающий контравариантность. public interface IMyContraVarGenIF { void Show(T obj);
}
// Реализовать интерфейс IMyContraVarGenlF. class MyClass : IMyContraVarGenIF {
public void Show(T x) { Console.WriteLine(x); }
}
// Создать простую иерархию классов, class Alpha {
public override string ToStringO { return "Это объект класса Alpha.";
}
//...
}
class Beta : Alpha {
public override string ToStringO { return "Это объект класса Beta.";
}
//...
}
class VarianceDemo { static void Main() {
// Создать ссылку из интерфейса IMyContraVarGenIF
//на объект типа MyClass.
// Это вполне допустимо как при наличии контравариантности, так и без нее. IMyContraVarGenIF AlphaRef = new MyClass();
// Создать ссылку из интерфейса IMyContraVarGenIF
// на объект типа MyClass.
//И это вполне допустимо как при наличии контравариантности,
// так и без нее.
IMyContraVarGenIF BetaRef = new MyClass();
// Создать ссылку из интерфейса IMyContraVarGenIF
//на объект типа MyClass.
// *** Это вполне допустимо благодаря контравариантности. *** IMyContraVarGenIF BetaRef2 = new MyClass();
// Этот вызов допустим как при наличии контравариантности, так и без нее. BetaRef.Show(new Beta());
// Присвоить переменную AlphaRef переменной BetaRef.
// *** Это вполне допустимо благодаря контравариантности. ***
BetaRef = AlphaRef;
BetaRef.Show(new Beta ());
}
}
Выполнение этой программы дает следующий результат.
Это объект класса Beta.
Это объект класса Beta.
Контравариантный интерфейс может быть расширен аналогично описанному выше расширению ковариантного интерфейса. Для достижения контравариантного характера расширенного интерфейса в его объявлении должен быть указан такой же параметр обобщенного типа, как и у базового интерфейса, но с ключевым словом in, как показано ниже.
public interface IMyContraVarGenIF2 : IMyContraVarGenIF {
//...
}
Следует иметь в виду, что указывать ключевое слово in в объявлении базового интерфейса не только не нужно, но и не допустимо. Более того, сам расширенный интерфейс IMyContraVarGenIF2 не обязательно должен быть контравариантным. Иными словами, обобщенный тип Т в интерфейсе IMyContraVarGenIF2 не требуется модифицировать ключевым словом in. Разумеется, все преимущества, которые сулит контравариантность в интерфейсе IMyContraVarGen, при этом будут утрачены в интерфейсе IMyContraVarGenIF2.
Читать дальше