Ограничения на параметры обобщенных подпрограмм и классов
По умолчанию с переменными, имеющими тип параметра обобщенного класса или подпрограммы, внутри методов обобщённых классов и обобщенных подпрограмм можно делать лишь ограниченный набор действий: присваивать и сравнивать на равенство (отметим, что в 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 := a[0];
for var i := 1 to a.High do
if a[i].CompareTo(min)<0 then
min := a[i];
Result := min;
end;
К сожалению, нет возможности использовать запись a[i]<min, поскольку операции не входят в интерфейсы.