Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSBasicCourse2ndedPodbelsky / CSBasicCourse2ndedPodbelsky.rtf
Скачиваний:
27
Добавлен:
22.03.2016
Размер:
11.9 Mб
Скачать

18.3. Ограничения типизирующих параметров

В приведённом выше формате объявления обобщённого класса указан один

весьма важный раздел, который определяет ограничения, накладываемые на

типизирующие аргументы. Он назван "ограничения_типизирующих_параметров

"типизирующие параметры:ограничения" ". Рассмотрим его назначение.

Во многих случаях обобщённый класс не только хранит значения, тип

которых определён типизирующим параметром, но и включает средства (обычно

это методы класса или его объектов) для обработки этих значений. Предположим,

что в экземплярах (в объектах) специализированных типов, порождаемых

обобщённым классом Stack<ItemType>, нужно хранить только такие данные, для

которых выполняется определённое условие, истинность которого устанавливается

с помощью метода CompareTo(). Проверку этого условия можно выполнять в

методе Push(), определив его, например, таким образом:

public void Push (ItemType data) {

if (((IComparable)data).CompareTo(default(ItemType)<0)…

return;

}

В теле метода Push() значение параметра data приводится к значению типа

интерфейса IComparable. Только после этого к результату приведения можно будет

применить метод CompareTo(), специфицированный в интерфейсе IComparable.

Этот интерфейс определён в пространстве System и предназначен для сравнения

объектов. Декларация интерфейса:

Intemface iComparable {

Int CompareTo (object p);

}

Как видите, интерфейс содержит прототип только одного метода. Назначение

метода CompareTo() – сравнивать значение того объекта, к которому он применён

(для которого вызван этот метод), со значением объекта-аргумента. Этот аргумент

заменяет параметр p, типом которого служит класс оbject. Так как оbject – общий

базовый класс для классов библиотек и программ на C#, то аргумент может иметь

любой тип. Предполагается, что целочисленный результат, возвращаемый методом

CompareTo( ), удовлетворяет следующим соглашениям.

Результат меньше нуля, если вызывающий объект нужно считать меньшим

нежели объект-параметр.

Результат больше нуля, если вызывающий объект нужно считать большим

нежели объект-параметр.

Результат равен нулю, если вызывающий объект нужно считать равным

объекту-параметру.

В методе Push() при обращении к методу CompareTo() в качестве аргумента

использовано особое выражение механизма обобщений default(ItemType).

Результат его вычисления – принятое по умолчанию значение того типа,

который будет замещать типизирующий параметр ItemType при создании

специализации (инстанцировании, конкретизации) обобщённого класса Stack<>.

После такого изменения метода Push() на основе обобщённого класса

Stack<ItemType> можно создавать сконструированные типы, используя только

типизирующие аргументы, классы которых реализовали интерфейс IComparable.

Предопределённые типы языка C#, такие как, например, int (System.Int32) или

double

(System.Double)

реализуют

интерфейс

IComparable.

При

других

типизирующих аргументах на этапе исполнения программы будет генерироваться

исключение InvalidCastException. Самое главное и плохое – допущенная ошибка в

использовании неверного типизирующего аргумента не будет распознаваться на

этапе компиляции.

Компилятор сможет идентифицировать неверное задание типизирующих

аргументов только в том случае, если в декларацию обобщённого класса включить

так называемый список ограничений (list of constaints) типизирующих параметров

"типизирующие параметры:список ограничений" .

Элемент этого списка имеет вид:

where имя_типизирующего_параметра:список_ограничений

Здесь where – служебное слово "служебное слово:where" языка C#, за ним

следует имя того типизирующего параметра обобщённого класса, для которого

вводятся ограничения. Список допустимых ограничений представляет собой список

обозначений типов, каждый из которых может использоваться в качестве

типизирующего аргумента, соответствующего данному параметру.

В нашем примере со стеком, помещать в который можно только значения, тип

которых допускает применение метода CompareTo(), обобщённый класс можно

определить так:

public class Stack <ItemType>

where ItemType:IComparable

{ private ItemType [ ] item=new ItemType[100];

public void Push(ItemType data) {

if (data.CompareTo(default(ItemType))<0) return;

}

public ItemType Pop( ) { …}

}

На основе такой декларации обобщённого класса компилятор будет проверять

все специализации и сообщать об ошибке в каждом случае, когда типизирующий

аргумент не является классом, реализовавшим интерфейс IСomparable. Обратите

внимание, что теперь в методе Push() нет приведения типа параметра к тип

у

интерфейса IComparable.

В данном примере обобщённого стека только один типизирующий параметр и

для него введено только одно ограничение – соответствующий типизирующий

аргумент должен быть именем класса, реализовавшего конкретный интерфейс

IComparable.

В обобщении может быть несколько типизирующих параметров и для каждого

из них может быть указано своё ограничение, вводимое служебным словом where.

В то же время для одного параметра в одном операторе where можно указать

несколько видов ограничений.

Таким образом, при определении обобщённого класса можно указать

ограничения, которым должны соответствовать типизирующие аргументы при

инстанцировании конкретного класса. Если в программе сделана попытка

сформировать

специализацию

обобщённого

класса

с

использованием

типизирующих аргументов, которые не соответствуют заданным ограничениям, то

результатом будет ошибка компиляции.

Определены пять видов ограничений:

where T: struct – типизирующий аргумент должен быть типом значений.

where T: class – типизирующий аргумент должен быть типом ссылок. Ссылки могут

относиться к любым классам, интерфейсам, делегатам и типам массивов.

where T: new() – типизирующий аргумент должен иметь конструктор без

параметров. Когда для одного типизирующего параметра используется

несколько ограничений, ограничение new() в списке должно быть последним.

where T: имя_класса – типизирующий аргумент должен быть либо указанным

классом, либо классом, производным от него.

where T:U, где в этой конструкции U – типизирующий параметр обобщения. При

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

аргумент должен быть либо тем же аргументом, который заменяет параметр

U, либо должен быть именем производного от него класса. Этот ви

д

ограничения называют ограничением с помощью явного типа (naked type

constraint).

Список ограничений типизирующих параметров позволяет компилятору

удостовериться, что операции и методы в теле обобщённого класса допустимы при

тех типизирующих аргументах, которые будут применяться при создании

специализаций обобщённого класса.

Типизирующие параметры, для которых ограничения не указаны, называют

свободными

(unbounded)

типизирующими

параметрами.

"типизирующие

параметры:свободные"

Для таких параметров требуется соблюдение следующих

требований:

Операции != и == нельзя использовать, так как нет уверенности, что конкретные

типизирующие аргументы будут поддерживать эти операции.

Они могут быть конвертированы к типу System.Object или преобразованы из

этого типа.

Они могут явно приводиться к любому интерфейсному типу.

Их можно сравнивать со значением null. Если свободный типизирующий

параметр сравнивается со значением null, то результат сравнения всегда false, когда

типизирующий аргумент является типом значений.

Задание ограничений с помощью явного типа полезно в тех случаях, когда

член

обобщённого

класса

имеет

собственный

типизирующий

параметр

"типизирующие параметры:свободные" , и необходимо, чтобы этот параметр

соответствовал типизирующему параметру обобщённого класса. Пример из MSDN:

class List <T> {

void Add<U> (List< U > item) where U:T { ...}

}

В данном примере обобщённый класс

List <T> включает в качестве члена

обобщённый метод Add<U>. (Обобщённым методам посвящён далее раздел 18.6)

.

Для типизирующего параметра U ограничение задано с помощью указания явного

типа T. Таким образом, T – ограничение (заданное указанием явного типа) в

обобщённом методе Add(). В то же время T в контексте объявления обобщённого

класса List <T> является свободным типизирующим параметром.

Ограничение с помощью указания явного типа может быть непосредственно

использоваться в объявлении обобщённого класса. Пример (из MSDN):

class SampleClass<T,U,V> where T:V { }

В данном примере требуется, чтобы аргумент, заменяющий параметр Т, был

типом, заменяющим параметр V, либо должен быть производным от него типом.

Полезность ограничений явным типом в объявлениях обобщённых классов

наиболее велика в тех случаях, когда необходимо подчеркнуть отношение

наследования между двумя типизирующими параметрами.

Перечисленные

пять

видов

ограничений

неравнозначны.

Первичными

ограничениями являются: имя_класса, class и struct.

Вторичные

ограничения :

имя _ интерфейса ,

типизирующий _ параметр

(ограничение с помощью указания явного типа).

Ограничения третьего уровня: new().

Именно в таком порядке (первое, второе, третье) ограничения могут входить в

оператор where для одного конкретного типизирующего параметра. При этом

первичное ограничение может быть только одно, число вторичных ограничений

может быть любым и ограничение третьего уровня, т.е. new(), может быть только

одно. Ограничения отделяются друг от друга в операторе where запятыми.

Ограничения одного параметра параметров (отдельные операторы where) никак не

разделяются (обычно их разделение обозначается пробельными символами).

При ограничении в виде имени класса:

этот класс не должен быть запечатан (sealed);

он не может иметь тип: Array, Delegate, Enum, Value Type;

он не может иметь тип оbject.

Примеры объявлений с ограничениями типизирующих параметров

:

Class MyClass < T > where T: class …

Class newClass <T, F> where T: Stream

where F:IComparable <int>, new() …

Соседние файлы в папке CSBasicCourse2ndedPodbelsky