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