使用泛型时如何处理Delphi Simple类型?

时间:2008-11-03 22:52:38

标签: delphi generics

一个小例子

TTest<T> = class
private
  f : T;
public
  function ToString : string;
end;

如果是一个对象,那么这应该有效

TTest<T>.ToString;
begin
  Result := f.ToString;
end;

但是当说整数时会发生什么?这在.net中可以。当然。

我知道它不起作用,但我如何编写它来处理对象和简单类型?

3 个答案:

答案 0 :(得分:7)

有三个原因可以解释为什么Delphi不会让你在第二个例子中做你想要做的事情 - 在一个无约束类型参数类型的值上调用ToString方法(或者至少这是我认为你正在尝试的要显示,因为TObject.ToString是一个实例方法,而不是一个类方法,所以T.ToString甚至不能用于TObject。)

  1. Delphi没有root类型系统,所有类型的通用操作都很少。这些操作 - 复制,赋值,创建位置(字段,局部,参数,数组) - 是唯一可以保证在类型参数的所有可能值上可用的操作。

  2. 从1开始,为什么操作仅限于这些?为什么不允许泛型类中的操作,只在实例化时给出错误?好吧,第一部分原因是该设计最初旨在最大限度地兼容.NET和dccil,因此.NET泛型不允许的东西在Win32泛型设计中没有显着的可用性。

  3. 设计的第二个理由是,在实例化时只检查是有问题的。使用这种方法的最着名的参数多态实现是C ++模板,它也以其神秘的错误消息而闻名,例如,尝试将错误的迭代器传递给算法,并得到关于未找到重载运算符的奇怪抱怨。多态性越深,问题就越严重。实际上,C ++本身就是以C ++ 0x Concepts的形式纠正这个错误,这太糟糕了。

  4. 希望您现在明白为什么不能保证通过约束无法保证可以使用的操作。但是,通过以方法引用或接口实现的形式提供操作,您可以相对轻松地摆脱此限制,如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函数,但它确实违背了泛型的目的。