泛型,多态,接口:解决方案是什么?

时间:2012-02-08 11:23:10

标签: delphi generics polymorphism delphi-xe2 covariance

我知道标题非常广泛 - 跨越很多!

我希望这个问题可能演变成一个更大的“信息wiki thingy”。

我所学到的知识 - 到目前为止:

  • 使用泛型时 - 了解概念(covariance and contravariance)。
  • Do NOT "mis-use"泛型的概念与继承相结合。我做了,它可以让你直接陷入协方差问题!确保你在继承中的正确位置“中断”通用 - 如果你要将两者结合起来。

(请纠正我 - 如果你认为我错了,遗失或误解了什么)。

我的问题是:

但到现在为止,我已经花了无数个小时,试图弄明白,如何解决我在桌面上的这个“大难题”。而且我已经从你们这些SO用户那里得到了一些很好的答案 - 但是现在是时候让他们有更大规模的工作了。

我用这个冒险进入泛型: Generics and Polymorphism working together

现在我有点坚持这个: Situations where Generics won't work

为什么我最终会出现协方差问题 - 是因为我的层次结构中的类程序。

所以我想知道Interfaces是否是我在这个“传奇”中的下一个大胆举动。 如何“跨越”协方差问题。 有一件事是要发现你确实遇到了这个问题 - 另一件事就是“如何解决它”。

因此,如果你们中的任何一个好人“在那里”对此有任何意见 - 我全都听见了。 基本上: 告诉我去接口(我从来没有从头开始做过)。 或者......按照你建议的方向向我扔骨头。

我当前的源池如第二个链接所述 - 从顶部开始。

这是我之前post的一小段代码,显示了我的协方差问题。 David善意地解释了 - 为什么我遇到灌木丛......但现在我需要了解 - 如何绕过它。

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;

此致

2 个答案:

答案 0 :(得分:5)

你无法做你想做的事,但这不是你如何使用泛型。正如Rob Kennedy所说,拥有TCustomerList<TCustomer>TPersonList<TPerson>是没有意义的。泛型的美妙之处在于您可以对不同的元素类型使用相同的列表。 这意味着列表和元素类型不得具有任何依赖关系。

您可以执行以下操作:

procedure TSomething.ProcessList<T: TBaseObject>(const aList: TBaseList<T>);
begin
  // process the list using code that is independent of the actual type of T.
end;

...

var
  aCustomerList: TBaseList<TCustomer>;
  aPersonList: TBaseList<TPerson>;
begin
  ProcessList(aCustomerList);
  ProcessList(aPersonList);

也许您可能必须指定T(某些早期版本的泛型不处理类型推断 - 即它从参数类型中推断出T的类型 - 非常好) ,即

  ProcessList<TCustomer>(aCustomerList);
  ProcessList<TPerson>(aPersonList);

但是,或者类似的东西,你应该做的。 IMO,其他任何事情都没有意义。没有必要拥有一个可以容纳任何这些列表的变量,例如aList。如果你真的需要一个,你只能使用TObject,但这不允许你以任何有用的方式使用列表。它不是很通用。

接口对这个问题根本无济于事。您可以通过接口(另一种多态)为类提供某些功能,即列表元素。但这不会处理协方差。

答案 1 :(得分:0)

我会选择:

TCustomCustomerList = class(TBaseList<TBaseObject>)
end;

TCustomerList = class(TCustomCustomerList)
end;

在您的设计中这是否可以接受是完全不同的事情。如果您要实现的目标是将TCustomerList分配给TBaseList变量,那么这将是最佳选择。