4

在 Delphi XE2 中,我想编写一个通用集合类来操作必须具有 Copy(owntype) 方法的对象,但我不知道如何最好地声明它。

我想要这样的例子(一个项目的集合,为简单起见):

//------ Library ------
Type
  TBaseCopyable = class
    S: string;
//    procedure Copy(OtherObject: TBaseCopyable); overload;
    procedure Copy(OtherObject: TBaseCopyable); virtual;
  end;

  MyCollection<T: TBaseCopyable, constructor> = class
    TheItem: T;
    procedure SetItem(AItem: T); 
    function  GetItem: T;
  end;

[...]

function MyCollection<T>.GetItem: T;
Var
  NewItem: T;
begin
  NewItem := T.Create;
  NewItem.Copy(TheItem);
  Result := NewItem;
end;


//------ Usage ------
Type
  TMyCopyable = class(TBaseCopyable)
    I: integer;
//  procedure Copy(OtherObject: TMyCopyable); overload;
    procedure Copy(OtherObject: TMyCopyable); override;
  end;

[...]
  Col: MyCollection<TMyCopyable>;

关键问题是,在 Col 中,我需要 MyCollection 的通用实现才能找到 TMyCopyable.Copy。不出所料,过载或虚拟都不能完成这项工作:

  • 重载时,代码会编译,但 MyCollection.GetItem 会找到 TBaseCopyable.Copy,而不是 TMyCopyable.Copy。
  • 使用 virtual/override 这不会编译,因为两个 Copy 声明的签名不匹配。

所以我想我需要在 TBaseCopyable 的规范中以某种方式使用泛型,可能而不是继承。但我不确定如何,主要是因为我并不特别需要将类型参数提供给 TBaseCopyable,我只需要 Copy 参数类型以通用方式引用“它自己的类的类型”。

想法?谢谢!

4

2 回答 2

6

变成TBaseCopyable一个 Generic 类并将其 Generic 类型应用于Copy(),然后TMyCopyable可以覆盖它,例如:

type
  TBaseCopyable<T> = class
    S: string;
    procedure Copy(OtherObject: T); virtual;
  end;

  MyCollection<T: TBaseCopyable<T>, constructor> = class
    TheItem: T;
    procedure SetItem(AItem: T);
    function  GetItem: T;
  end;

type
  TMyCopyable = class(TBaseCopyable<TMyCopyable>)
    I: integer;
    procedure Copy(OtherObject: TMyCopyable); override;
  end;

或者,只需执行相同的TPersistent.Assign()操作(因为它不使用泛型):

type
  TBaseCopyable = class
    S: string;
    procedure Copy(OtherObject: TBaseCopyable); virtual;
  end;

  MyCollection<T: TBaseCopyable, constructor> = class
    TheItem: T;
    procedure SetItem(AItem: T);
    function  GetItem: T;
  end;

type
  TMyCopyable = class(TBaseCopyable)
    I: integer;
    procedure Copy(OtherObject: TBaseCopyable); override;
  end;

procedure TMyCopyable.Copy(OtherObject: TBaseCopyable);
begin
  inherited;
  if OtherObject is TMyCopyable then
    I := TMyCopyable(OtherObject).I;
end;
于 2013-09-22T03:40:43.543 回答
0

回答我自己的问题,或者至少总结一下发现:

据我所知,我提出的问题没有完整的答案。我学到的是:

[1] 如果基项类(此处为 TBaseCopyable)没有状态,并且要么是抽象的,要么方法不需要引用相同类型的其他对象,Remy 的解决方案就是要走的路。(例如: TBaseCopyable 没有字段,只有抽象方法。)

[2] 一个重要的问题是如何指定一个泛型类,其后代类可以指定方法参数并返回与其封闭类相同类型的值。在 Remy 的示例中,这是在后代类声明中完成的:

      TMyCopyable = 类(TBaseCopyable<TMyCopyable>)

这意味着在泛型类中,T 将被最终感兴趣的类所取代。

[3] 但是,在 TBaseCopyable 的泛型声明中,T 始终是 TBaseCopyable 的信息不可用,因此在 TBaseCopyable 的实现中,对 T 类型对象的引用将无法看到 TBaseCopyable 的方法或字段。

如果我们可以在 T 上设置一个约束来告诉编译器 T 是 TBaseCopyable,这将得到解决。

这显然是 C# 中的方法:http: //blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

在 Delphi 中,我认为会是这样的:

  类型
    TBaseCopyable<T: TBaseCopyable<T> > = 类
    ...

就像 Remy 为 MyCollection 表演的节目一样。但是,该语法在同一个类声明中是不合法的(错误:未声明的标识符 TBaseCopyable),因为 TBaseCopyable 尚未完全定义。我们可能会考虑为 TBaseCopyable 创建一个前向声明(就像我们为非泛型类所做的那样),但这会引发错误,并且显然编译器不支持它:

[4] 也许泛型类可以继承实现?

如果我们这样做会怎样:

  类型
    TBaseCopyable<T> = 类(TBaseCopyableImpl) ...

这将允许 TBaseCopyable 拥有一些可以相互引用的字段和方法。但是,即使这些方法是虚拟的,它们也会将固定的参数/返回类型强加给后代,避免这种情况是首先使用泛型的基本原理。

所以这个策略只适用于不需要专门研究后代类型的字段和方法......例如对象计数器。

结论

这个问题原来与众所周知的“Curiously recurring template pattern”有关:http ://en.wikipedia.org/wiki/Curiously_recurring_template_pattern 。尽管看起来一个人试图完成的事情很简单,但背后存在理论问题。

这种情况似乎需要一个语言关键字,意思是“与我的封闭类相同的类型”。然而,这显然会导致协变/逆变问题——违反了继承层次结构中哪些类型可以替代哪些类型的规则。也就是说,似乎 Delphi 并没有像 C# 那样允许尽可能多的部分解决方案。

当然,我很高兴知道还有更进一步的方法!

哦,我不觉得很难找到这个问题的底部 - 甚至肯阿诺德认为这很困难:https ://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid.html #comment-828994

:-)

于 2013-09-24T02:11:00.377 回答