Страница 301 из 502
<b> return "Это объект класса Alpha.";</b>
<b> }</b>
<b> // ...</b>
<b>}</b>
<b>class Beta : Alpha {</b>
<b> public override string ToString() {</b>
<b> return "Это объект класса Beta.";</b>
<b> }</b>
<b> // ...</b>
<b>}</b>
<b>class VarianceDemo {</b>
<b> static void Main() {</b>
<b> // Создать ссылку из интерфейса IMyContraVarGenIF<Alpha></b>
<b> //на объект типа MyClass<Alpha>.</b>
<b> // Это вполне допустимо как при наличии контравариантности, так и без нее. </b>
<b> IMyContraVarGenIF<Alpha> AlphaRef = new MyClass<Alpha>();</b>
<b> // Создать ссылку из интерфейса IMyContraVarGenIF<beta></b>
<b> // на объект типа MyClass<Beta>.</b>
<b> //И это вполне допустимо как при наличии контравариантности,</b>
<b> // так и без нее.</b>
<b> IMyContraVarGenIF<Beta> BetaRef = new MyClass<Beta>();</b>
<b> // Создать ссылку из интерфейса IMyContraVarGenIF<beta></b>
<b> //на объект типа MyClass<Alpha>.</b>
<b> // *** Это вполне допустимо благодаря контравариантности. ***</b>
<b> IMyContraVarGenIF<Beta> BetaRef2 = new MyClass<Alpha>();</b>
<b> // Этот вызов допустим как при наличии контравариантности, так и без нее.</b>
<b> BetaRef.Show(new Beta());</b>
<b> // Присвоить переменную AlphaRef переменной BetaRef.</b>
<b> // *** Это вполне допустимо благодаря контравариантности. ***</b>
<b> BetaRef = AlphaRef;</b>
<b> BetaRef.Show(new Beta());</b>
<b> }</b>
<b>}</b>
Выполнение этой программы дает следующий результат.
<b>Это объект класса Beta.</b>
<b>Это объект класса Beta.</b>
Контравариантный интерфейс может быть расширен аналогично описанному выше расширению ковариантного интерфейса. Для достижения контравариантного характера расширенного интерфейса в его объявлении должен быть указан такой же параметр обобщенного типа, как и у базового интерфейса, но с ключевым словом in, как показано ниже.
<b>public interface IMyContraVarGenIF2<in Т> : IMyContraVarGenIF<T> {</b>
<b> // ...</b>
<b>}</b>
Следует иметь в виду, что указывать ключевое слово in в объявлении базового интерфейса не только не нужно, но и не допустимо. Более того, сам расширенный интерфейс IMyContraVarGenIF2 не обязательно должен быть контравариантным. Иными словами, обобщенный тип Т в интерфейсе IMyContraVarGenIF2 не требуется модифицировать ключевым словом in. Разумеется, все преимущества, которые сулит контравариантность в интерфейсе IMyContraVarGen, при этом будут утрачены в интерфейсе IMyContraVarGenIF2.
Контравариантность оказывается пригодной только для ссылочных типов, а параметр контравариантного типа можно применять только к аргументам методов. Следовательно, ключевое слово in нельзя указывать в параметре типа, используемом в качестве возвращаемого типа.
Как пояснялось в главе 15, ковариантность и контравариантность поддерживается в необобщенных делегатах в отношении типов, возвращаемых методами, и типов, указываемых при объявлении параметров. Начиная с версии C# 4.0, возможности ковариантности и контравариантности были распространены и на обобщенные делегаты. Подобные возможности действуют таким же образом, как было описано выше в отношении обобщенных интерфейсов.
Ниже приведен пример контравариантного делегата.
<b>// Объявить делегат, контравариантный по отношению к обобщенному типу Т.</b>
<b>delegate bool SomeOp<in Т>(Т obj);</b>
Этому делегату можно присвоить метод с параметром обобщенного типа Т или же класс, производный от типа Т.
А вот пример ковариантного делегата.
<b>// Объявить делегат, ковариантный по отношению к обобщенному типу Т.</b>
<b>delegate Т AnotherOp<out Т, V>(V obj);</b>
Этому делегату можно присвоить метод, возвращающий обобщенный тип Т, или же класс, производный от типа Т. В данном случае V оказывается просто параметром инвариантного типа.
В следующем примере программы демонстрируется применение обоих разновидностей вариантных делегатов на практике.
<b>// Продемонстрировать конвариантность и контравариантность</b>
<b>// в обобщенных делегатах.</b>
<b>using System;</b>
<b>// Объявить делегат, контравариантный по отношению к обобщенному типу Т.</b>