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

на главную

Жанры

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

В данном случае оба типа, Т и V, заменяются типом int. В итоге оба варианта мето да Set оказываются совершенно одинаковыми, что, разумеется, приводит к ошибке. Следовательно, при последующей попытке вызвать метод Set для объекта notOK в методе Main появится сообщение об ошибке вследствие неоднозначности во время компиляции.

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

типа

В главе 15 ковариантность и контравариантность были рассмотрены в связи с не обобщенными делегатами. Эта форма ковариантности и контравариантности по- прежнему поддерживается в С#, поскольку она очень полезна. Но в версии C# 4.0 воз можности ковариантности и контравариантности были расширены до параметров обобщенного типа, применяемых в обобщенных интерфейсах и делегатах. Ковариант ность и контравариантность применяется, главным образом, для рационального разре шения особых ситуаций, возникающих в связи с применением обобщенных интерфей сов и делегатов, определенных в среде .NET Framework. И поэтому некоторые интер фейсы и делегаты, определенные в библиотеке, были обновлены, чтобы использовать ковариантность и контравариантность параметров типа. Разумеется, преимуществами ковариантности и контравариантности можно также воспользоваться в интерфейсах и делегатах, создаваемых собственными силами. В этом разделе механизмы ковариант ности и контравариантности параметров типа поясняются на конкретных примерах. Применение ковариантности в обобщенном интерфейсе

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

Для того чтобы стали понятнее последствия применения ковариантности, обратим ся к конкретному примеру. Ниже приведен очень простой интерфейс IMyCoVarGenIF, в котором применяется ковариантность. // В этом обобщенном интерфейсе поддерживается ковариантность. public interface IMyCoVarGenIF<out Т> { Т GetObject; }

Обратите особое внимание на то, как объявляется параметр обобщенного типа Т. Его имени предшествует ключевое слово out. В данном контексте ключевое слово out обозначает, что обобщенный тип Т является ковариантным. А раз он ковариантный, то метод GetObject может возвращать ссылку на обобщенный тип Т или же ссылку на любой класс, производный от типа Т.

Несмотря на свою ковариантность по отношению к обобщенному типу Т, интер фейс IMyCoVarGenIF реализуется аналогично любому другому обобщенному интер фейсу. Ниже приведен пример реализации этого интерфейса в классе MyClass. // Реализовать интерфейс IMyCoVarGenIF. class MyClass<T> : IMyCoVarGenIF<T> { T obj; public MyClass(T v) { obj = v; } public T GetObject { return obj; } }

Обратите внимание на то, что ключевое слово out не указывается еще раз в выраже нии, объявляющем реализацию данного интерфейса в классе MyClass. Это не только не нужно, но и вредно, поскольку всякая попытка еще раз указать ключевое слово out будет расцениваться компилятором как ошибка.

А теперь рассмотрим следующую простую реализацию иерархии классов. // Создать простую иерархию классов. class Alpha { string name; public Alpha(string n) { name = n; } public string GetName { return name; } // ... } class Beta : Alpha { public Beta(string n) : base(n) { } // ... }

Как видите, класс Beta является производным от класса Alpha. С учетом всего изложенного выше, следующая последовательность операций будет считаться вполне допустимой. // Создать ссылку из интерфейса IMyCoVarGenIF на объект типа MyClass<Alpha>. // Это вполне допустимо как при наличии ковариантности, так и без нее. IMyCoVarGenIF<Alpha> AlphaRef = new MyClass<Alpha>(new Alpha("Alpha #1")); Console.WriteLine("Имя объекта, на который ссылается переменная AlphaRef: " + AlphaRef.GetObject.GetName); // А теперь создать объект MyClass<Beta> и присвоить его переменной AlphaRef. // *** Эта строка кода вполне допустима благодаря ковариантности. *** AlphaRef = new MyClass<Beta>(new Beta("Beta #1")); Console.WriteLine("Имя объекта, на который теперь ссылается " + "переменная AlphaRef: " + AlphaRef.GetObject.GetName);

Прежде всего, переменной AlphaRef типа IMyCoVarGenIF в этом фраг менте кода присваивается ссылка на объект типа MyClass. Это вполне допу стимая операция, поскольку в классе MyClass реализуется интерфейс IMyCoVarGenIF, причем и в том, и в другом в качестве аргумента типа указывается Alpha. Далее имя объекта выводится на экран при вызове метода GetName для объекта, возвращаемо го методом GetObject. И эта операция вполне допустима, поскольку Alpha — это и тип, возвращаемый методом GetName, и обобщенный тип Т. После этого пере менной AlphaRef присваивается ссылка на экземпляр объекта типа MyClass, что также допустимо, потому что класс Beta является производным от класса Alpha, а обобщенный тип Т — ковариантным в интерфейсе IMyCoVarGenIF. Если бы любое из этих условий не выполнялось, данная операция оказалась бы недопустимой. Ради большей наглядности примера вся рассмотренная выше последовательность операций собрана ниже в единую программу. // Продемонстрировать ковариантность в обобщенном интерфейсе. using System; // Этот обобщенный интерфейс поддерживает ковариантность. public interface IMyCoVarGenIF<out Т> { Т GetObject; } // Реализовать интерфейс IMyCoVarGenIF. class MyClass<T> : IMyCoVarGenIF<T> { T obj; public MyClass(T v) { obj = v; } public T GetObject { return obj; } } // Создать простую иерархию классов. class Alpha { string name; public Alpha(string n) { name = n; } public string GetName { return name; } // ... } class Beta : Alpha { public Beta(string n) : base(n) { } // ... } class VarianceDemo { static void Main { // Создать ссылку из интерфейса IMyCoVarGenIF на объект типа MyClass<Alpha>. // Это вполне допустимо как при наличии ковариантности, так и без нее. IMyCoVarGenIF<Alpha> AlphaRef = new MyClass<Alpha>(new Alpha("Alpha #1")); Console.WriteLine("Имя объекта, на который ссылается переменная " + "AlphaRef: " + AlphaRef.GetObject.GetName); // А теперь создать объект MyClass<Beta> и присвоить его // переменной AlphaRef. // *** Эта строка кода вполне допустима благодаря ковариантности. *** AlphaRef = new MyClass<Beta>(new Beta("Beta #1")); Console.WriteLine("Имя объекта, на который теперь ссылается переменная " + "AlphaRef: " + AlphaRef.GetObject.GetName); } }

Результат выполнения этой программы выглядит следующим образом. Имя объекта, на который ссылается переменная AlphaRef: Alpha #1 Имя объекта, на который теперь ссылается переменная AlphaRef: Beta #1

Следует особо подчеркнуть, что переменной AlphaRef можно присвоить ссылку на объект типа MyClass благодаря только тому, что обобщенный тип Т указан как ковариантный в интерфейсе IMyCoVarGenIF. Для того чтобы убедиться в этом, удалите ключевое слово out из объявления параметра обобщенного типа Т в интер фейсе IMyCoVarGenIF и попытайтесь скомпилировать данную программу еще раз. Компиляция завершится неудачно, поскольку строгая проверка на соответствие типов не разрешит теперь подобное присваивание.

Один обобщенный интерфейс может вполне наследовать от другого. Иными сло вами, обобщенный интерфейс с параметром ковариантного типа можно расширить, как показано ниже. public interface IMyCoVarGenIF2<out Т> : IMyCoVarGenIF<T> { // ... }

Обратите внимание на то, что ключевое слово out указано только в объявлении рас ширенного интерфейса. Указывать его в объявлении базового интерфейса не только не нужно, но и не допустимо. И последнее замечание: обобщенный тип Т допускается не указывать как ковариантный в объявлении интерфейса IMyCoVarGenIF2. Но при этом исключается ковариантность, которую может обеспечить расширенный интерфейс IMyCoVarGetIF. Разумеется, возможность сделать интерфейс IMyCoVarGenIF2 инва риантным может потребоваться в некоторых случаях его применения.

На применение ковариантности накладываются некоторые ограничения. Ковари антность параметра типа может распространяться только на тип, возвращаемый ме тодом. Следовательно, ключевое слово out нельзя применять в параметре типа, слу жащем для объявления параметра метода. Ковариантность оказывается пригодной только для ссылочных типов. Ковариантный тип нельзя использовать в качестве огра ничения в интерфейсном методе. Так, следующий интерфейс считается недопустимым. public interface IMyCoVarGenIF2<out Т> { void M<V> where V:T; // Ошибка, ковариантный тип T нельзя // использовать как ограничение } Применение контравариантности в обобщенном интерфейсе

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

СД. Том 17

Клеванский Кирилл Сергеевич
17. Сердце дракона
Фантастика:
боевая фантастика
6.70
рейтинг книги
СД. Том 17

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

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

Шипучка для Сухого

Зайцева Мария
Любовные романы:
современные любовные романы
8.29
рейтинг книги
Шипучка для Сухого

Мимик нового Мира 3

Северный Лис
2. Мимик!
Фантастика:
юмористическая фантастика
постапокалипсис
рпг
5.00
рейтинг книги
Мимик нового Мира 3

Мимик нового Мира 6

Северный Лис
5. Мимик!
Фантастика:
юмористическая фантастика
попаданцы
рпг
5.00
рейтинг книги
Мимик нового Мира 6

Деспот

Шагаева Наталья
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Деспот

Помещица Бедная Лиза

Шах Ольга
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Помещица Бедная Лиза

Небо для Беса

Рам Янка
3. Самбисты
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Небо для Беса

Полководец поневоле

Распопов Дмитрий Викторович
3. Фараон
Фантастика:
попаданцы
5.00
рейтинг книги
Полководец поневоле

"Дальние горизонты. Дух". Компиляция. Книги 1-25

Усманов Хайдарали
Собрание сочинений
Фантастика:
фэнтези
боевая фантастика
попаданцы
5.00
рейтинг книги
Дальние горизонты. Дух. Компиляция. Книги 1-25

Колючка для высшего эльфа или сиротка в академии

Жарова Анита
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Колючка для высшего эльфа или сиротка в академии

Последний попаданец 8

Зубов Константин
8. Последний попаданец
Фантастика:
юмористическая фантастика
рпг
5.00
рейтинг книги
Последний попаданец 8

Протокол "Наследник"

Лисина Александра
1. Гибрид
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Протокол Наследник

Огненный князь

Машуков Тимур
1. Багряный восход
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Огненный князь