DELPHI:泛型和多态

Bim*_*r_R 10 delphi generics polymorphism covariance delphi-xe2

这已被问过几种不同的方式 - 但我还没有找到答案.

有人可以为我澄清一些事情.使用:Delphi XE2

我有一个很大的BaseObject,几乎用于所有东西.与它一起,我有一个通用列表 - BaseList.

延迟是这样的:

TBaseObject = class
... a lot of properties and methods ...
end;

TBaseList<T: TBaseObject> = class(TObjectList<T>)
... some properties and methods ... 
end;
Run Code Online (Sandbox Code Playgroud)

我最近尝试使用Objects []属性将TBaseList声明从一个非常旧的TStringList更改为这个永远不会更通用的泛型列表TObjectList.

但是我遇到了一些问题.BaseUnit是一个文件......每次我下载我的BaseObject时,我都会制作一个专门的列表来跟随它.

所以我会去做类似的事情:

TCustomer = class(TBaseObject)
... customer related stuff ...
end;

TCustomerList<T: TCustomer> = class(TBaseList<T>)
... customer list specific stuff ...
end;
Run Code Online (Sandbox Code Playgroud)

但是现在我希望一个对象包含一个列表 - 它可以容纳任何对象.我想我可以这样做

TControlObject = class(TBaseobject)
  FGenList: TBaseList<TBaseObject>; 
end;
Run Code Online (Sandbox Code Playgroud)

由于BaseList和BaseObject是我的层次结构的顶层,我假设这样的List能够保存我能想到的任何列表.

但我有一种感觉,就是在这里,我失败了...一种TBaseList<TBaseobject>在某种程度上无法与TCustomerList<TCustomer>...... 相提并论......即使TCustomerList并且TCustomer是我的基地的后代.

我希望能够在baselist中使用泛型来实现新对象的安装.即.使用T.Create在populate方法.

以下是完整层次结构的示例:

Base Unit;
TBaseObject = class
end;
TBaseList<T:TBaseObject> = class(TObjectList<T>)
end;

CustomCustomer Unit;
TCustomCustomer = class(TBaseObject) 
end;
TCustomCustomerList<T:TCustomCustomer> = class(TBaseList<T>)
end;

Customer Unit;
TCustomer = class(TCustomCustomer)
end;
TCustomerList<T:TCustomer> = class(TCustomCustomerList<T>)
end;

CustomPerson Unit;
TCustomPerson = class(TBaseObject) 
end;
TCustomPersonList<T:TCustomPerson> = class(TBaseList<T>)
end;

Person Unit;
TPerson = class(TCustomPerson)
end;
TPersonList<T:TPerson> = class(TCustomPersonList<T>)
end;
Run Code Online (Sandbox Code Playgroud)

鉴于上述层次结构 - 为什么我不能:

var    
  aList : TBaseList<TBaseObject>;  // used as a list parameter for methods
  aPersonList : TPersonList<TPerson>;
  aCustomerList : TCustomerList<TCustomer>;
begin
  aPersonList := TPersonList<TPerson>.Create;
  aCustomerList := TCustomerList<TCustomer>.Create;

  aList := aCustomerList;  <-- this FAILS !!  types not equal ..

end;
Run Code Online (Sandbox Code Playgroud)

调用处理所有列表的基类的过程失败的方式相同...

Procedure LogStuff(SomeList : TBaseList<TBaseObject>)
begin
  writeln(Format( 'No. Elements in list : %d',[SomeList.Count]));
end;
Run Code Online (Sandbox Code Playgroud)

有人可以打我,告诉我这里我做错了什么吗?

Dav*_*nan 16

Delphi泛型不支持协方差和逆变,因此使用该语言的当前语法无法实现您尝试做的事情.我建议您阅读以下博客文章,更详细地介绍此事.

从根本上说,你试图做的是:

type
  TBase = class;
  TDerived = class(TBase);
  TBaseList = TList<TBase>;
  TDerivedList = TList<TDerived>;
var
  BaseList: TBaseList;
  DerivedList: TDerivedList;
...
BaseList := TDerivedList;//does not compile
Run Code Online (Sandbox Code Playgroud)

设计师并没有阻止你这样做.有一个很好的理由.请考虑以下标准示例:

type
  TAnimal = class;
  TCat = class(TAnimal);
  TPenguin = class(TAnimal);

var
  AnimalList: TList<TAnimal>;
  CatList: TList<TCat>;
  Penguin: TPenguin;
...
AnimalList := CatList;//does not compile because...
AnimalList.Add(Penguin);//...of the danger of this
Run Code Online (Sandbox Code Playgroud)

虽然将a添加TPenguin到a 是合理的TList<TAnimal>,但实际列表AnimalList中指的是a TList<TCat>和企鹅不是猫.

而且,如果您想在示例层次结构的上下文中考虑它,这里是一个证明语言设计合理性的代码示例.

aList := aCustomerList;//does not compile
aList.Add(aCustomPerson);
//this would add a TCustomPerson instance to a list containing 
//TCustomer instances, but a TCustomPerson is not a TCustomer
Run Code Online (Sandbox Code Playgroud)

  • 阅读了你提供的链接 - 我必须说...谢谢.这些信息应该包含在泛型的帮助中 - 特别是TObjectList! (5认同)