Небольшой пример
TTest<T> = class
private
f : T;
public
function ToString : string;
end;
Если это объект, то это должно работать
TTest<T>.ToString;
begin
Result := f.ToString;
end;
Но что происходит, если, скажем, целое число? Это было бы нормально в .net. конечно.
Я знаю, что это не сработает, но как мне написать код для работы с объектами И простыми типами?





Последний пример работать не будет. Вам нужно добавить ограничение, чтобы использовать методы. В этом случае будет достаточно TObject:
TTest<T: TObject>.ToString;
begin
Result := T.ToString;
end;
Вы можете использовать простые типы с неограниченными обобщениями, но вы очень ограничены в использовании. Потому что единственные допустимые операции - это присваивание и сравнение (равно и не равно).
В Delphi простые типы не являются классами, поэтому у них нет методов. Но вы можете сделать следующее:
type
TToString<T> = reference to function(const AValue: T): string;
TGenContainer<T> = class
private
FValue: T;
FToString : TToString<T>;
public
constructor Create(const AToString: TToString<T>);
function ToString: string;
property Value: T read FValue write FValue;
end;
constructor TGenContainer<T>.Create(const AToString: TToString<T>);
begin
FToString := AToString;
end;
function TGenContainer<T>.ToString: string;
begin
Result := FToString(FValue);
end;
procedure TForm2.Button1Click(Sender: TObject);
var
gen : TGenContainer<Integer>;
begin
gen := TGenContainer<Integer>.Create(
function(const AValue: Integer): string
begin
Result := IntToStr(AValue);
end);
try
gen.Value := 17;
Memo1.Lines.Add(gen.ToString);
finally
gen.Free;
end;
end;
Это прекрасно работает.
да, я знаю это, но как мне заставить его работать. Он отлично работает в .net, потому что все является объектом.
Думаю, я мог бы обернуть свои простые типы объектами и переопределить функцию ToString, но это противоречит цели дженериков.
Ага, у простых типов нет методов.
Есть три причины, по которым Delphi не позволяет вам делать то, что вы пытаетесь сделать во втором примере - вызывать метод ToString для значения типа параметра неограниченного типа (или, по крайней мере, то, что я думаю, вы пытались показать, поскольку TObject.ToString - это метод экземпляра, а не метод класса, поэтому T.ToString не будет работать даже для TObject).
В Delphi нет системы корневых типов, и очень мало операций, общих для всех типов. Эти операции - копирование, присвоение, создание местоположений (полей, локальных переменных, параметров, массивов) - единственные операции, которые гарантированно доступны для всех возможных значений параметра типа.
Исходя из пункта 1, почему операции ограничиваются этим? Почему бы не разрешить операции в универсальном классе и выдать ошибки только во время создания экземпляра? Что ж, первая часть причины заключается в том, что дизайн изначально был предназначен для максимальной совместимости с .NET и dccil, поэтому вещи, которые не допускались универсальными шаблонами .NET, не имели существенных возможностей в дизайне универсальных шаблонов Win32.
Второе обоснование дизайна состоит в том, что проверка только во время создания экземпляра проблематична. Самая известная реализация параметрического полиморфизма, использующая этот подход, - это шаблоны C++, и она также известна своими загадочными сообщениями об ошибках, как вы, например, попробуйте передать в алгоритм неправильный тип итератора и получите странные жалобы на то, что перегруженные операторы не найдены. Чем глубже полиморфизм, тем хуже проблема. На самом деле, это так плохо, что C++ сам исправляет эту ошибку в виде концепций C++ 0x.
Надеюсь, теперь вы понимаете, почему нельзя использовать операции, доступность которых не гарантируется через ограничения. Однако вы можете относительно легко обойти это ограничение, предоставив операцию в форме ссылки на метод или реализации интерфейса, как предлагает Gamecat.
В будущем универсальные шаблоны в Delphi для Win32, вероятно, будут расширены по аналогии с концепциями C++ 0x или классами типов Haskell, так что параметры типа могут быть ограничены доступными определенными методами, функциями и операторами. Если он идет по линиям классов типов, то вывод типа может продолжаться таким же образом.
T - это тип, а не значение. Ваш код должен выглядеть примерно так: var x: T; begin Результат: = x.ToString; конец;