Ограничения на параметры обобщенных подпрограмм и классов

Ограничения на параметры обобщенных подпрограмм и классов

По умолчанию с переменными, имеющими тип параметра обобщенного класса или подпрограммы, внутри методов обобщённых классов и обобщенных подпрограмм можно делать лишь ограниченный набор действий: присваивать и сравнивать на равенство (отметим, что в NET сравнение на равенство внутри обобщений запрещено!).

Например, данный код будет работать:

function Eq<T>(a,b: T): boolean;

begin

Result := a = b;

end;

Можно также использовать присваивание переменной, имеющей тип параметра обобщенного класса или подпрограммы, значение по умолчанию, используя конструкцию default(T) - значение по умолчанию для типа T (nil для ссылочных типов и нулевое значение для размерных типов):

procedure Def<T>(var a: T);

begin

a := default(T);

end;

Однако, данный код

function Sum<T>(a,b: T): T;

begin

Result := a + b;

end;

вызовет ошибку компиляции до инстанцирования (создания экземпляра с конкретным типом). Такое поведение в .NET кардинально отличается от шаблонов в C++, где в коде шаблона можно использовать любые операции с шаблонными параметрами, и ошибка может произойти только в момент инстанцирования с конкретным типом.

Чтобы разрешить использование некоторых действий с переменными, имеющими тип параметра обобщенного класса или подпрограммы, используются ограничения на обобщенные параметры, задаваемые в секции where после заголовка подпрограммы или класса:

type

MyPair<T> = class

where T: System.ICloneable;

private

x,y: T;

public

constructor (x,y: T);

begin

Self.x := x;

Self.y := y;

end;

function Clone: MyPair;

begin

Result := new MyPair<T>(x.Clone,y.Clone);

end;

end;

В секции where через запятую перечисляются следующие ограничения:

На 1 месте: слово class или слово record или имя класса-предка.

На 2 месте: список реализуемых интерфейсов через запятую.

На 3 месте: слово constructor, указывающее, что данный тип должен иметь конструктор по умолчанию.

При этом каждое из мест, кроме одного, может быть пустым.

Для каждого типа-параметра может быть своя секция where, каждая секция where завершается точкой с запятой.

Пример. Обобщенная функция поиска минимального элемента в массиве. Элементы должны реализовывать интерфейс IComparable<T>.

function MinElem<T>(a: array of T): T;

where T: IComparable<T>;

begin

var min: T := a[0];

for var i := 1 to a.Length-1 do

if min.CompareTo(a[i])<0 then

min := a[i];

Result := max;

end;

К сожалению, нет возможности использовать операцию <, поскольку операции не входят в интерфейсы.