如何在派生类型中使用泛型方法

时间:2013-10-22 10:41:17

标签: delphi generics inheritance delphi-xe3

这看起来相当简单,也许我只是缺少一些语法粘合...这是我的简单通用(Delphi XE3)示例:

unit Unit1;

interface

uses
  generics.collections;

type

X = class
public
  Id: Integer;
end;

XList<T : X> = class( TObjectList<T> )
 function Find(Id: Integer) : T;
end;

Y = class(X)

end;

YList = class(XList<Y>)
end;

implementation

{ XList<T> }

function XList<T>.Find(Id: Integer): T;
var
  t: X;
begin
  for t in Self do
    if t.Id = Id then
      Result := t;
end;

end.

这不会用“[dcc32错误] Unit1.pas(41)编译:E2010不兼容的类型:'Y'和'X'”。这是下线:

YList = class(XList<Y>)
end;

Y来自X,为什么会出现问题?

2 个答案:

答案 0 :(得分:8)

Alex的答案是解决问题的正确方法。一旦知道答案,它也可以很好地从函数返回。

我想通过更多解释来扩展答案。特别是我想回答你在评论中对Alex的答案提出的问题:

  

顺便说一句......为什么原来的工作没有? T来自X。

问题代码在这里:

function XList<T>.Find(Id: Integer): T;
var
  t: X;
begin
  for t in Self do
    if t.Id = Id then
      Result := t;
end;

考虑泛型的方法是想象在实例化类型并提供具体类型参数时代码的样子。在这种情况下,我们将T替换为Y。然后代码如下所示:

function XList_Y.Find(Id: Integer): Y;
var
  t: X;
begin
  for t in Self do
    if t.Id = Id then
      Result := t;
end;

现在,您在分配给Result的行中遇到问题:

Result := t;

嗯,Result的类型为Y,但t的类型为XXY之间的关系是Y来自X。因此Y的实例是X。但X的实例不是Y。所以作业无效。

正如Alex正确指出的那样,您需要将循环变量声明为T类型。就个人而言,我会写这样的代码:

function XList<T>.Find(Id: Integer): T;
begin
  for Result in Self do
    if Result.Id = Id then
      exit;
  Result := nil;
  // or perhaps you wish to raise an exception if the item cannot be found
end;

这也解决了搜索例程在未找到项目时未初始化其返回值的问题。这是一个问题,一旦你获得了实际编译的代码,编译器就会警告过这个问题。我希望你启用编译器警告,并在它们出现时处理它们!

答案 1 :(得分:6)

我必须按照以下方式重新实现Find方法来修复它:

{ XList<T> }

function XList<T>.Find(Id: Integer): T;
var
  item: T;
begin
  for item in Self do
    if item.Id = Id then
      Exit(item);
  Result := nil;
end;

这里重要的是将变量声明中使用的类型从X替换为T

然后我只是将变量从t重命名为item,以避免与类型占位符T发生名称冲突,并将Result := item替换为Exit(item)返回找到的项目并退出方法。