Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Ограничения на параметры универсальных типов



Как правило, универсальные типы не просто хранят данные, но и вызывают методы у объекта, чей тип указан как параметр. Например, в классе Dictionary<K, V> метод Add() может использовать метод CompareTo() для сравнения ключей:

public class Dictionary<K, V>

{

public void Add(K key, V value)

{

...

if (key.CompareTo(x) < 0) {... } // ошибка компиляции!

...

}

}

Ошибка компиляции в этом примере возникает по следующей причине. Так как тип K может быть любым, то у параметра key можно вызывать только методы, определённые в object. Проблему можно решить, используя приведение типов:

public class Dictionary<K, V>

{

public void Add(K key, V value)

{

...

if (((IComparable) key).CompareTo(x) < 0) {... }

...

}

}

Недостаток такого подхода – многочисленность операций приведения. К тому же, если у сконструированного типа параметр K не поддерживает интерфейс IComparable, то при работе программы будет сгенерировано исключение InvalidCastException.

C# допускает указание ограничений (constraints) для каждого параметра универсального типа. Только тип, удовлетворяющий ограничениям, может быть применён для записи сконструированного типа.

Ограничения делятся на первичные ограничения, вторичные ограничения и ограничения конструктора. Первичное ограничение – это тип, который не является sealed, за исключением System.Object, System.Array, System.Delegate, System.MulticastDelegate, System.ValueType, System.Enum или System.Void. Первичное ограничение требует, чтобы аргумент сконструированного типа приводился к указанному типу.

Существуют два особых первичных ограничения – class и struct. Ограничению class удовлетворяет любой ссылочный тип – класс, интерфейс, делегат. Ограничению struct удовлетворяет любой тип значения, за исключением типов с поддержкой null.

Вторичное ограничение – это интерфейс. Вторичное ограничение требует, чтобы аргумент сконструированного типа реализовывал указанный интерфейс.

Ограничение конструктора имеет вид new() и требует, чтобы аргумент сконструированного типа имел конструктор без параметров.

Ограничения объявляются с использованием ключевого слова where, после которого указывается параметр универсального типа, двоеточие и список ограничений:

– ноль или одно первичное ограничение;

– ноль или несколько вторичных ограничений;

– ноль или одно ограничение конструктора (если не задано первичное ограничение struct).

В следующем примере демонстрируется использование ограничений на различные параметры универсального типа:

public class EntityTable<K, E>

where K: IComparable<K>, IPersistable

where E: Entity, new()

{

public void Add(K key, E entity)

{

...

if (key.CompareTo(x) < 0) {... }

...

}

}

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

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

.

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

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

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

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

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

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

public interface IOutOnly<out T>

{

T this[int index] { get; }

}

public interface IInOnly<in T>

{

void Process(T x);

}

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





Дата публикования: 2015-11-01; Прочитано: 417 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.007 с)...