Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык C# и основы платформы .NET.docx
Скачиваний:
36
Добавлен:
11.05.2015
Размер:
178.68 Кб
Скачать

18.4. Ковариантность и контравариантность

Определим понятия ковариантности и контравариантности для сконструированных типов данных. Для этого введём отношение частичного порядка на множестве ссылочных типов:

наследуется (прямо или косвенно) от.

Если имеется тип C<T>, а также типыT1иT2(T1 ≤ T2), тоC<T>назовём:

ковариантным, еслиC<T1> ≤ C<T2>;

контравариантным, еслиC<T2> ≤ C<T1>;

инвариантным, если не верно ни первое, ни второе утверждение.

Понятия частичного порядка типов, ковариантности и контравариантности связаны с приведением типов. Тот факт, что тип T1«меньше» типаT2, означает возможность неявного приведения переменной типаT1к типуT2. Как указывалось, массивы ковариантны для ссылочных типов (например, массив строк присваивается массиву объектов).

Покажем на примере, что универсальные классы не могут быть ни ковариантными, ни контравариантными. Рассмотрим следующий набор классов:

// два простых класса, связанных наследованием

public class Person

{

publicstringName;

}

publicclassStudent:Person

{

public string University;

}

// универсальный класс с открытым полем

publicclassC<T>

{

publicT Field;

}

// объявление и инициализация двух объектов универсального класса

varcp =newC<Person>();

varcs =newC<Student> {Field =newStudent()};

Допустим, разрешена ковариантность классов. Тогда последняя строка в следующем листинге вызовет ошибку доступа к памяти, так как фактически объекту класса-предка выполняется присваивание объекта класса-потомка.

// это присваивание было бы возможным при ковариантности

cp = cs;

cp.Field = newPerson();

// в следующей строке – ошибка доступа к памяти

cs.Field.University = "MIT";

Из аналогичных соображений не может быть разрешена контравариантность классов, т. е. присваивание cs = cp:

// это присваивание было бы возможным при контравариантности

cs = cp;

vars = cs.Field;

// в следующей строке – ошибка доступа к памяти

s.University = "Berkeley";

Таким образом, универсальные классы инвариантны, однако универсальные интерфейсы могут быть описаны как ковариантные или контравариантные относительно некоего параметра-типа. Чтобы указать на ковариантность относительно параметра T, следует использовать ключевое словоoutпри описании параметра типа. На контравариантность указывает ключевое словоinпри описании параметра типа.

publicinterfaceIOutOnly<outT>

{

T this[intindex] {get; }

}

publicinterfaceIInOnly<inT>

{

void Process(T x);

}

Для обеспечения безопасности типов компилятор отслеживает, чтобы ковариантные параметры всегда использовались как типы возвращаемых значений, а контравариантные параметры являлись типами аргументов. Один универсальный интерфейс может, при необходимости, содержать как ковариантные, так и контравариантные параметры.

19. Использование универсальных шаблонов

В данном параграфе приведено несколько примеров использования универсальных шаблонов в элементах платформы .NET.

19.1. Кортежи

В языках программирования кортежем(tuple) называется структура данных, содержащая фиксированный набор разнотипных значений. В платформе .NET для создания кортежей доступен набор универсальных классов видаSystem.Tuple<>. Всего имеется восемь универсальных классовTuple<>, которые различаются количеством параметров типов.

Tuple<string,int> t =newTuple<string,int>("Hello", 4);

Console.WriteLine("{0} - {1}", t.Item1, t.Item2);

Статический класс System.Tupleсодержит восемь перегруженных версий методаCreate()для конструирования кортежа с заданным числом элементов:

Tuple<string,int> t =Tuple.Create("Hello", 4);