7

这看起来相当简单,也许我只是缺少一些语法胶水......这是我简单的通用(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 Error] Unit1.pas(41): E2010 Incompatible types: 'Y' and 'X'”一起编译。归根结底:

YList = class(XList<Y>)
end;

Y 派生自 X 那么为什么会有问题呢?

4

2 回答 2

8

亚历克斯的答案是问题的正确解决方案。一旦知道答案,从函数返回也很好。

我想通过更多解释来扩展答案。特别是,我想回答您在对亚历克斯的回答的评论中提出的问题:

顺便说一句……为什么原来的工作不起作用?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;

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

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是 type Y,但是t是 type XX和之间的关系YY源自X。所以一个实例Y是一个X。但是 的实例X不是Y. 所以这个赋值是无效的。

正如 Alex 正确指出的那样,您需要将循环变量声明为 type 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;

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

于 2013-10-22T12:02:23.047 回答
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;

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

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

于 2013-10-22T11:17:49.747 回答