Создание класса-прототипа

Язык С# позволяет создавать собственные классы-прототипы и их разновидно­сти — интерфейсы, структуры, делегаты и события, а также обобщенные (generic) методы обычных классов.

Рассмотрим создание класса-прототипа на примере стека, приведенном в специ­фикации С#. Параметр типа данных, которые хранятся в стеке, указывается в уг­ловых скобках после имени класса, а затем используется таким же образом, как и обычные типы:

public class Stack<T>

{

Т[] items;

int count;

public void Push(T item){...} // помещение в стек

public T Pop() {... } // извлечение из стека

}

При использовании этого класса на место параметра Т подставляется реальный тип, например int:

Stack<int> stack = new Stack<int>();

stack.Push(3);

int x = stack.Pop();

Тип Stack<int> называется сконструированным типом (constructed type). Этот тип создается во время выполнения программы при его первом упоминании в программе. Если в программе встретится класс Stack с другим значимым ти­пом, например double, среда выполнения создаст другую копию кода для этого типа. Напротив, для всех ссылочных типов будет использована одна и та же копия кода, поскольку работа с указателями на различные типы выполняется одинако­вым образом. Создание конкретного экземпляра класса-прототипа называется инстанцированием.

Класс-прототип может содержать произвольное количество параметров типа. Для каждого из них могут быть заданы ограничения (constraints), указывающие, каким требованиям должен удовлетворять аргумент, соответствующий этому па­раметру, например, может быть указано, что это должен быть значимый тип или тип, который реализует некоторый интерфейс.

Синтаксически ограничения задаются после ключевого слова where, например:

public class Stack<T>

where T: struct

{ …

Здесь с помощью слова struct записано ограничение, что элементы стека долж­ны быть значимого типа. Для ссылочного типа употребляется ключевое слово class. Для каждого типа, являющегося параметром класса, может быть задана одна строка ограничений, которая может включать один класс, а за ним — произ­вольное количество интерфейсов, перечисляемых через запятую.

Указание в качестве ограничения имени класса означает, что соответствующий аргумент при инстанцировании может быть именем либо этого класса, либо его потомка. Указание имени интерфейса означает, что тип-аргумент должен реа-лизовывать данный интерфейс - это позволяет использовать внутри класса-про­тотипа, например, операции по перечислению элементов, их сравнению и т. п.

Помимо класса и интерфейсов в ограничениях можно задать требование, чтобы тип-аргумент имел конструктор по умолчанию без параметров, что позволяет создавать объекты этого типа в теле методов класса-прототипа. Это требование записывается в виде выражения new(), например:

public class EntityTable<K.E>

where К: IComparable<K>, IPersistable

where E: Entity, new()

{

public void Add(К key, E entity)

{

//...

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

}

}

В этом примере на первый аргумент класса EntityTable накладываются два огра­ничения по интерфейсам, а для второго аргумента задано, что он может быть только классом Entity или его потомком и иметь конструктор без параметров.

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

Для задания значений по умолчанию типизированным элементам класса-прото­типа используется ключевое слово default. С его помощью элементам ссылочного типа присваивается значение null, а элементам значимого типа — 0.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: