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