的一个小例子
TTest<T> = class
private
f : T;
public
function ToString : string;
end;
如果是一个对象,那么这应该工作
TTest<T>.ToString;
begin
Result := f.ToString;
end;
但如果是整数,会发生什么呢?这在.net中当然可以。
我知道它不起作用,但我如何编写代码来处理对象和简单类型?
Delphi不允许您执行第二个示例中尝试执行的操作有三个原因-对无约束类型参数类型的值调用ToString方法(或者至少我认为这是您试图显示的,因为TObject.ToString是一个实例方法,而不是类方法,所以t.ToString即使对TObject也不起作用)。
-
Delphi没有一个根类型系统,而且几乎没有所有类型通用的操作。这些操作——复制、赋值、创建位置(字段、局部变量、参数、数组)——是唯一保证对类型参数的所有可能值可用的操作。
-
从1开始,为什么操作仅限于这些?为什么不允许泛型类中的操作,并且只在实例化时给出错误?原因的第一部分是,该设计最初旨在最大限度地提高与.NET和dccil的兼容性,因此.NET泛型不允许的东西在Win32泛型设计中没有显著的可供性。
-
设计的第二个理由是,只在实例化时进行检查是有问题的。使用这种方法的最著名的参数多态性实现是C++模板,它也以其神秘的错误消息而闻名,例如,当你试图将错误类型的迭代器传递给算法,并收到关于找不到重载运算符的奇怪抱怨时。多态性越深,问题就越严重。事实上,C++本身正在以C++0x概念的形式纠正这个错误,这太糟糕了。
希望您现在能够理解为什么不能通过约束来保证可用的操作不能被使用。然而,正如Gamecat所建议的,通过以方法引用或接口实现的形式提供操作,您可以相对容易地摆脱这种限制。
将来,用于Win32的Delphi中的泛型可能会按照类似的方式扩展到C++0x Concepts或Haskell类型类,这样类型参数可能会被限制为具有某些可用的方法、函数和运算符。如果它沿着类型类的路线进行,那么类型推理也可能以这种方式进行。
最后一个例子不起作用。您需要添加一个约束才能使用方法。在这种情况下,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;
这很好用。
我想我可以用对象包装我的简单类型,并覆盖ToString函数,但它确实违背了泛型的目的。