Чтение онлайн

на главную - закладки

Жанры

Полное руководство. С# 4.0
Шрифт:

Применительно к обобщенному интерфейсу контравариантность служит сред ством, разрешающим методу использовать аргумент, тип которого относится к базо вому классу, указанному в соответствующем параметре типа. В прошлом тип аргу мента метода должен был в точности соответствовать параметру типа в силу строгой проверки обобщений на соответствие типов. Контравариантность смягчает это строгое правило таким образом, чтобы обеспечить типовую безопасность. Параметр контрава риантного типа объявляется с помощью ключевого слова in, которое предваряет имя этого параметра.

Для того чтобы стали понятнее последствия применения ковариантности, вновь обратимся к конкретному примеру. Ниже приведен обобщенный интерфейс 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);

Поделиться:
Популярные книги

Девяностые приближаются

Иванов Дмитрий
3. Девяностые
Фантастика:
попаданцы
альтернативная история
7.33
рейтинг книги
Девяностые приближаются

Громовая поступь. Трилогия

Мазуров Дмитрий
Громовая поступь
Фантастика:
фэнтези
рпг
4.50
рейтинг книги
Громовая поступь. Трилогия

Пипец Котенку!

Майерс Александр
1. РОС: Пипец Котенку!
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Пипец Котенку!

Мама из другого мира...

Рыжая Ехидна
1. Королевский приют имени графа Тадеуса Оберона
Фантастика:
фэнтези
7.54
рейтинг книги
Мама из другого мира...

Надуй щеки!

Вишневский Сергей Викторович
1. Чеболь за партой
Фантастика:
попаданцы
дорама
5.00
рейтинг книги
Надуй щеки!

Идеальный мир для Лекаря 15

Сапфир Олег
15. Лекарь
Фантастика:
боевая фантастика
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 15

Спасение 6-го

Уолш Хлоя
3. Парни из школы Томмен
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Спасение 6-го

По осколкам твоего сердца

Джейн Анна
2. Хулиган и новенькая
Любовные романы:
современные любовные романы
5.56
рейтинг книги
По осколкам твоего сердца

Медиум

Злобин Михаил
1. О чем молчат могилы
Фантастика:
фэнтези
7.90
рейтинг книги
Медиум

Я тебя не предавал

Бигси Анна
2. Ворон
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Я тебя не предавал

Найди меня Шерхан

Тоцка Тала
3. Ямпольские-Демидовы
Любовные романы:
современные любовные романы
короткие любовные романы
7.70
рейтинг книги
Найди меня Шерхан

Хроники сыска (сборник)

Свечин Николай
3. Сыщик Его Величества
Детективы:
исторические детективы
8.85
рейтинг книги
Хроники сыска (сборник)

(Не) моя ДНК

Рымарь Диана
6. Сапфировые истории
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
(Не) моя ДНК

Никто и звать никак

Ром Полина
Фантастика:
фэнтези
7.18
рейтинг книги
Никто и звать никак