C# 4.0 полное руководство - 2011
Шрифт:
public interface IMyCoVarGenIF2<out Т> {
void M<V> where V:T; // Ошибка, ковариантный тип T нельзя
// использовать как ограничение
}
Применение контравариантности в обобщенном интерфейсе
Применительно к обобщенному интерфейсу контравариантность служит сред- . ством, разрешающим методу использовать аргумент, тип которого относится к базовому классу, указанному в соответствующем параметре типа. В прошлом тип аргумента метода должен был в точности соответствовать
Для того чтобы стали понятнее последствия применения ковариантности, вновь обратимся к конкретному примеру. Ниже приведен обобщенный интерфейс IMyContraVarGenlF контравариантного типа. В нем указывается контравариантный параметр обобщенного типа Т, который используется в объявлении метода Show .
// Это обобщенный интерфейс, поддерживающий контравариантность. public interface IMyContraVarGenIF<in Т> { void Show(T obj);
}
Как видите, обобщенный тип Т указывается в данном интерфейсе как контравариантный с помощью ключевого слова in, предшествующего имени его параметра. Обратите также внимание на то, что Т является параметром типа для аргумента obj в методе Show .
• Далее интерфейс IMyContraVarGenlF реализуется в классе MyClass, как показано ниже.
// Реализовать интерфейс IMyContraVarGenlF. 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 несколько отличаются от аналогичных классов
С учетом всего изложенного выше, следующая последовательность операций будет считаться вполне допустимой. }
// Создать ссылку из интерфейса IMyContraVarGenIF<Alpha>
//на объект типа MyClass<Alpha>.
// Это вполне допустимо как при наличии контравариантности, так и без нее. IMyContraVarGenIF<Alpha> AlphaRef = new MyClass<Alpha>;
// Создать ссылку из интерфейса IMyContraVarGenIF<beta>
// на объект типа MyClass<Beta>.
//И это вполне допустимо как при наличии контравариантности, так и без нее. IMyContraVarGenIF<Beta> BetaRef = new MyClass<Beta>;
// Создать ссылку из интерфейса IMyContraVarGenIF<beta>
7/ на объект типа MyClass<Alpha>.
// *** Это вполне допустимо благодаря контравариантности. *** IMyContraVarGenIF<Beta> BetaRef2 = new MyClass<Alpha>;
// Этот вызов допустим как при наличии контравариантности, так и без нее.
BetaRef.Show(new Beta);
// Присвоить переменную AlphaRef переменной BetaRef.
// *** Это вполне допустимо благодаря контравариантности. ***
BetaRef = AlphaRef;
BetaRef.Show(new Beta );
Прежде всего, обратите внимание на создание двух переменных ссылочного типа IMyContraVarGenlF, которым присваиваются ссылки на объекты класса MyClass, где параметры типа совпадают с аналогичными параметрами в интерфейсных ссылках. В первом случае используется параметр типа Alpha, а во втором — параметр типа Beta. Эти объявления не требуют контравариантности и допустимы в любом случае.
Далее создается переменная ссылочного типа IMyContraVarGenIF<Beta>, но на этот раз ей присваивается ссылка на объект класса MyClass<Alpha>. Эта операция вполне допустима, поскольку обобщенный тип Т объявлен как контравариантный.
Как и следовало ожидать, следующая строка, в которой вызывается метод BetaRef. Show с аргументом Beta, является вполне допустимой. Ведь Beta — это обобщенный тип Т в классе MyClass<Beta> и в то же время аргумент в методе Show .