在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。不出所料,过载或虚拟都不能完成这项任务:
所以我认为我需要在TBaseCopyable的规范中以某种方式使用泛型,可能代替继承。但我不确定如何,主要是因为我不特别需要将类型参数提供给TBaseCopyable,我只需要复制参数类型以通用方式引用“它自己类的类型”。
想法?谢谢!
答案 0 :(得分: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;
答案 1 :(得分:0)
回答我自己的问题,或者至少总结一下结果:
据我所知,在我提出问题时,问题没有完整的答案。我所学到的是:
[1]如果基类项(此处为TBaseCopyable)没有状态,并且要么是抽象的,要么方法不需要引用相同类型的其他对象,所以Remy的解决方案是可行的方法。 (例如:TBaseCopyable没有字段,只有抽象方法。)
[2]一个重要的问题是如何指定一个泛型类,其后代类可以指定方法参数并返回与其封闭类相同类型的值。在Remy的例子中,这是在后代类声明中完成的:
TMyCopyable = class(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中,我认为会是这样的:
type TBaseCopyable<T: TBaseCopyable<T> > = class ...像雷米为MyCollection展示的那样。但是,该语法在同一个类声明中是不合法的(错误:未声明的标识符TBaseCopyable),因为TBaseCopyable尚未完全定义。我们可能会考虑为TBaseCopyable创建一个前向声明(就像我们对非泛型类一样),但这会引发错误,显然编译器不支持它:
How to set a forward declaration with generic types under Delphi 2010?
E2086错误,前缀声明类型为http://qc.embarcadero.com/wc/qcmain.aspx?d=94044
[4]也许泛型类可以继承实现?
如果我们这样做了:
type TBaseCopyable<T> = class(TBaseCopyableImpl) ...
这将允许TBaseCopyable具有可以相互引用的一些字段和方法。但是,即使这些方法是虚拟的,它们也会对后代强加固定的参数/返回类型,避免使用泛型的基本原理。
因此,此策略仅适用于不需要专门处理后代类型的字段和方法...例如对象计数器。
结论
这个问题最近引起了人们对“奇怪的反复出现的模板模式”的关注: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
: - )