【问题标题】:Delphi generics TObjectList<T> inheritanceDelphi 泛型 TObjectList<T> 继承
【发布时间】:2013-02-06 05:21:10
【问题描述】:

我想创建一个TObjectList&lt;T&gt; 后代来处理我的应用程序中对象列表之间的通用功能。然后我想从那个新类中进一步下降,以便在需要时引入额外的功能。我似乎无法使用超过 1 级的继承来使其工作。我可能需要更多地了解泛型,但我一直在寻找正确的方法来做到这一点,但没有成功。到目前为止,这是我的代码:

unit edGenerics;

interface

uses
  Generics.Collections;

type

  TObjectBase = class
  public
    procedure SomeBaseFunction;
  end;

  TObjectBaseList<T: TObjectBase> = class(TObjectList<T>)
  public
    procedure SomeOtherBaseFunction;
  end;

  TIndexedObject = class(TObjectBase)
  protected
    FIndex: Integer;
  public
    property Index: Integer read FIndex write FIndex;
  end;

  TIndexedObjectList<T: TIndexedObject> = class(TObjectBaseList<T>)
  private
    function GetNextAutoIndex: Integer;
  public
    function Add(AObject: T): Integer;
    function ItemByIndex(AIndex: Integer): T;
    procedure Insert(AIndex: Integer; AObject: T);
  end;

  TCatalogueItem = class(TIndexedObject)
  private
    FID: integer;
  public
    property ID: integer read FId write FId;
  end;

  TCatalogueItemList = class(TIndexedObjectList<TCatalogueItem>)
  public
    function GetRowById(AId: Integer): Integer;
  end;

implementation

uses
  Math;

{ TObjectBase }

procedure TObjectBase.SomeBaseFunction;
begin
end;

{ TObjectBaseList<T> }

procedure TObjectBaseList<T>.SomeOtherBaseFunction;
begin
end;

{ TIndexedObjectList }

function TIndexedObjectList<T>.Add(AObject: T): Integer;
begin
  AObject.Index := GetNextAutoIndex;
  Result := inherited Add(AObject);
end;

procedure TIndexedObjectList<T>.Insert(AIndex: Integer; AObject: T);
begin
  AObject.Index := GetNextAutoIndex;
  inherited Insert(AIndex, AObject);
end;

function TIndexedObjectList<T>.ItemByIndex(AIndex: Integer): T;
var
  I: Integer;
begin
  Result := Default(T);
  while (Count > 0) and (I < Count) and (Result = Default(T)) do
    if Items[I].Index = AIndex then
      Result := Items[I]
    else
      Inc(I);
end;

function TIndexedObjectList<T>.GetNextAutoIndex: Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to Count - 1 do
    Result := Max(Result, Items[I].Index);
  Inc(Result);
end;

{ TCatalogueItemList }

function TCatalogueItemList.GetRowById(AId: Integer): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Pred(Self.Count) do
    if Self.Items[I].Id = AId then
    begin
      Result := I;
      Break;
    end;
end;

end.
/////// ERROR HAPPENS HERE ////// ???? why is beyond me

似乎有以下声明:

>>> TCatalogueItemList = class(TIndexedObjectList<TCatalogueItem>) <<<<

导致以下编译器错误:

[DCC 错误] edGenerics.pas(106): E2010 不兼容的类型: 'TCatalogueItem' 和 'TIndexedObject'

但是编译器在编译单元的 END 处(第 106 行)显示错误,而不是在声明本身上,这对我来说没有任何意义......

基本上我的想法是我有一个从 TObjectList 下降的通用列表,我可以根据需要使用新功能进行扩展。对此的任何帮助都会很棒!!!

我应该使用 Delphi 2010 添加。

谢谢。

【问题讨论】:

  • 如果您能指出导致错误的代码的实际部分,那就太好了(因为您可以使用它)。这比期望我们计算或复制/粘贴到编辑器中要容易得多。像// error happens here//this is line 106 这样的简单评论就足够了。
  • 对问题正文添加了进一步的解释
  • 不应该 TIndexedObjectList&lt;T: TIndexedObject&gt;TObjectBaseList&lt;T: TObjectBase&gt; 下降而不是 TObjectList&lt;T&gt; 在您当前的代码中?
  • 是的,应该感谢您接受它。但是在对声明进行更正后,它与之前的编译错误完全相同。
  • @Ken:XE3 IDE 将错误定位在文件的最后一行(最后结束之后)。错误消息中没有提供行号。

标签: delphi generics delphi-2010


【解决方案1】:

你的错误在于类型转换,编译器错误是可以的(但它无法在我的 Delphi XE3 中找到正确的文件)。

您的 ItemByIndex 方法已声明:

TIndexedObjectList<T>.ItemByIndex(AIndex: Integer): T;

但是你有这行:

Result := TIndexedObject(nil);

这对于 parentTIndexedObjectList 来说很好,其中函数的结果是 TIndexedObject 类型,但对于后代类就不行了 @ 987654325@,其中函数的结果类型为TCatalogueItem

您可能知道,TCatalogueItem 实例与TIndexedObject 变量的赋值兼容,但反之则不然。它翻译成这样的:

function TCatalogueItemList.ItemByIndex(AIndex: Integer): TCatalogueItem;
begin
  Result := TIndexedObject(nil);  //did you see the problem now?

要将结果初始化为 nil 值,可以调用 Default() 伪函数,如下所示:

Result := Default(T);

在 Delphi XE 或更高版本中,该解决方案也是通用的。与其将结果类型转换为固定的 TIndexedObjectList 类,不如使用 T 类型应用泛型类型转换

Result := T(nil);
//or 
Result := T(SomeOtherValue);

但是,在这种特定情况下,不需要对nil 常量进行类型转换,因为nil 是一个与任何引用兼容的特殊值,因此您只需将行替换为:

Result := nil;

它会编译,并希望按您的预期工作。

【讨论】:

  • 非常感谢您的回答。它几乎可以工作,但行:Result := nil 导致编译错误Incompatible types: 'T' and 'Pointer'。这就是为什么我之前将它类型转换为 TIndexedObject。如何将泛型类型的 Result 设置为 nil 或泛型等效项?
  • 在 XE3 中它可以工作,但如果您的编译器需要强制转换,请使用通用类型:Result := T(nil);,如答案中所述。
  • 感谢您的建议,但不幸的是,在 Delphi 2010 中,声明 Result := T(nil); 会产生 Invalid typecast 错误。因此,这两种方法似乎都不适用于 Delphi 2010。有趣的是,如果我完全注释掉该行,它编译时不会出错。我将对此进行进一步测试,看看当项目不在列表中时它是否返回 nil。
  • 在 Delphi 2010 中(可能是以后?),为函数结果初始化泛型类型的正确方法是 Result := Default(T);。这似乎适用于上述功能。
  • @Rick 和 jachguate:感谢Default(T)。学到了一些新东西。
猜你喜欢
  • 1970-01-01
  • 2014-12-19
  • 2011-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-04
相关资源
最近更新 更多