Литмир - Электронная Библиотека
Содержание  
A
A

Для того чтобы стали понятнее последствия применения ковариантности,вновь обратимся к конкретному примеру. Ниже приведен обобщенный интерфейсIMyContraVarGenIF контравариантного типа. В нем указывается контравариантныйпараметр обобщенного типа Т, который используется в объявлении метода Show().// Это обобщенный интерфейс, поддерживающий контравариантность.public interface IMyContraVarGenIF<in Т> { void Show(T obj);}

Как видите, обобщенный тип Т указывается в данном интерфейсе как контравариантный с помощью ключевого слова in, предшествующего имени его параметра.Обратите также внимание на то, что Т является параметром типа для аргумента objв методе Show().

Далее интерфейс IMyContraVarGenIF реализуется в классе MyClass, как показанониже.// Реализовать интерфейс IMyContraVarGenIF.class MyClass<T> : IMyContraVarGenIF<T> { public void Show(T x) { Console.WriteLine(x); }}

В данном случае метод Show() просто выводит на экран строковое представлениепеременной х, получаемое в результате неявного обращения к методу ToString() изметода WriteLine().

После этого объявляется иерархия классов, как показано ниже.// Создать простую иерархию классов.class Alpha { public override string ToString() { return "Это объект класса Alpha."; } // ...}class Beta : Alpha { public override string ToString() { return "Это объект класса Beta."; } // ...}

Ради большей наглядности классы Alpha и Beta несколько отличаются от аналогичных классов из предыдущего примера применения ковариантности. Обратите также внимание на то, что метод ToString() переопределяется таким образом, чтобывозвращать тип объекта.

С учетом всего изложенного выше, следующая последовательность операций будетсчитаться вполне допустимой.// Создать ссылку из интерфейса IMyContraVarGenIF<Alpha>// на объект типа MyClass<Alpha>.// Это вполне допустимо как при наличии контравариантности, так и без нее.IMyContraVarGenIF<Alpha> AlphaRef = new MyClass<Alpha>();// Создать ссылку из интерфейса IMyContraVarGenIF<beta>// на объект типа MyClass<Beta>.// И это вполне допустимо как при наличии контравариантности, так и без нее.IMyContraVarGenIF<Beta> BetaRef = new MyClass<Beta>();// Создать ссылку из интерфейса IMyContraVarGenIF<beta>// на объект типа MyClass<Alpha>.// *** Это вполне допустимо благодаря контравариантности. ***IMyContraVarGenIF<Beta> BetaRef2 = new MyClass<Alpha>();// Этот вызов допустим как при наличии контравариантности, так и без нее.BetaRef.Show(new Beta());// Присвоить переменную AlphaRef переменной BetaRef.// *** Это вполне допустимо благодаря контравариантности. ***BetaRef = AlphaRef;BetaRef.Show(new Beta());

Прежде всего, обратите внимание на создание двух переменных ссылочного типаIMyContraVarGenIF, которым присваиваются ссылки на объекты класса MyClass, гдепараметры типа совпадают с аналогичными параметрами в интерфейсных ссылках.В первом случае используется параметр типа Alpha, а во втором — параметр типаBeta. Эти объявления не требуют контравариантности и допустимы в любом случае.

Далее создается переменная ссылочного типа IMyContraVarGenIF, но наэтот раз ей присваивается ссылка на объект класса MyClass. Эта операциявполне допустима, поскольку обобщенный тип Т объявлен как контравариантный.

Как и следовало ожидать, следующая строка, в которой вызывается метод BetaRef.Show() с аргументом Beta, является вполне допустимой. Ведь Beta — это обобщенный тип Т в классе MyClass и в то же время аргумент в методе Show().

В следующей строке переменная AlphaRef присваивается переменной BetaRef.Эта операция вполне допустима лишь в силу контравариантности. В данном случаепеременная относится к типу MyClass, а переменная AlphaRef — к типуMyClass. Но поскольку Alpha является базовым классом для класса Beta, тотакое преобразование типов оказывается допустимым благодаря контравариантности.Для того чтобы убедиться в необходимости контравариантности в рассматриваемомздесь примере, попробуйте удалить ключевое слово in из объявления обобщенноготипа Т в интерфейсе IMyContraVarGenIF, а затем попытайтесь скомпилировать приведенный выше код еще раз. В результате появятся ошибки компиляции.

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

Выполнение этой программы дает следующий результат.Это объект класса Beta.Это объект класса Beta.

Контравариантный интерфейс может быть расширен аналогично описанномувыше расширению ковариантного интерфейса. Для достижения контравариантногохарактера расширенного интерфейса в его объявлении должен быть указан такой жепараметр обобщенного типа, как и у базового интерфейса, но с ключевым словом in,как показано ниже.public interface IMyContraVarGenIF2<in Т> : IMyContraVarGenIF<T> { // ...}

Следует иметь в виду, что указывать ключевое слово in в объявлении базовогоинтерфейса не только не нужно, но и не допустимо. Более того, сам расширенныйинтерфейс IMyContraVarGenIF2 не обязательно должен быть контравариантным.Иными словами, обобщенный тип Т в интерфейсе IMyContraVarGenIF2 не требуется модифицировать ключевым словом in. Разумеется, все преимущества, которыесулит контравариантность в интерфейсе IMyContraVarGen, при этом будут утраченыв интерфейсе IMyContraVarGenIF2.

Контравариантность оказывается пригодной только для ссылочных типов, а параметр контравариантного типа можно применять только к аргументам методов. Следовательно, ключевое слово in нельзя указывать в параметре типа, используемом вкачестве возвращаемого типа.Вариантные делегаты

Как пояснялось в главе 15, ковариантность и контравариантность поддерживаетсяв необобщенных делегатах в отношении типов, возвращаемых методами, и типов, указываемых при объявлении параметров. Начиная с версии C# 4.0, возможности ковариантности и контравариантности были распространены и на обобщенные делегаты.Подобные возможности действуют таким же образом, как было описано выше в отношении обобщенных интерфейсов.

Ниже приведен пример контравариантного делегата.// Объявить делегат, контравариантный по отношению к обобщенному типу Т.delegate bool SomeOp<in Т>(Т obj);

Этому делегату можно присвоить метод с параметром обобщенного типа Т или жекласс, производный от типа Т.

135
{"b":"245736","o":1}