Delphi generics TObjectList<T> inheritance
我想创建一个 TObjectList<T> 后代来处理我的应用程序中对象列表之间的通用功能。然后我想从那个新类中进一步下降,以便在需要时引入额外的功能。我似乎无法使用超过 1 级的继承来使其工作。我可能需要更多地了解泛型,但我已经从高处和低处搜索了正确的方法来做到这一点,但没有成功。到目前为止,这是我的代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
unit edGenerics;
interface uses type TObjectBase = class TObjectBaseList<T: TObjectBase> = class(TObjectList< T >) TIndexedObject = class(TObjectBase) TIndexedObjectList<T: TIndexedObject> = class(TObjectBaseList< T >) TCatalogueItem = class(TIndexedObject) TCatalogueItemList = class(TIndexedObjectList<TCatalogueItem>) implementation uses { TObjectBase } procedure TObjectBase.SomeBaseFunction; { TObjectBaseList< T > } procedure TObjectBaseList< T >.SomeOtherBaseFunction; { TIndexedObjectList } function TIndexedObjectList< T >.Add(AObject: T): Integer; procedure TIndexedObjectList< T >.Insert(AIndex: Integer; AObject: T); function TIndexedObjectList< T >.ItemByIndex(AIndex: Integer): T; function TIndexedObjectList< T >.GetNextAutoIndex: Integer; { TCatalogueItemList } function TCatalogueItemList.GetRowById(AId: Integer): Integer; end. |
似乎有以下声明:
1
|
>>> TCatalogueItemList = class(TIndexedObjectList<TCatalogueItem>) <<<<
|
导致以下编译器错误:
[DCC Error] edGenerics.pas(106): E2010 Incompatible types:
‘TCatalogueItem’ and ‘TIndexedObject’
但是编译器在编译单元的末尾(第 106 行)显示错误,而不是在声明本身上,这对我来说没有任何意义…
基本上我的想法是我有一个从 TObjectList 下降的通用列表,我可以根据需要扩展新功能。对此的任何帮助都会很棒!!!
我应该使用 Delphi 2010 添加。
谢谢。
- 如果您能指出导致错误的代码的实际部分,那就太好了(因为您可以使用它)。这比期望我们计算或复制/粘贴到编辑器中要容易得多。像 // error happens here 或 //this is line 106 这样的简单注释就足够了。
- 对问题主体添加了进一步的解释
- TIndexedObjectList<T: TIndexedObject> 不应该像您当前的代码那样从 TObjectBaseList<T: TObjectBase> 而不是从 TObjectList<T> 下降吗?
- 是的,它应该感谢您选择它。但是,在对声明进行更正后,它与之前的编译错误完全相同。
- @Ken:XE3 IDE 将错误定位在文件的最后一行(最后一行之后)。错误消息中没有提供行号。
您的错误在于类型转换,编译器错误是可以的(但它无法在我的 Delphi XE3 中找到正确的文件)。
您的 ItemByIndex 方法已声明:
1
|
TIndexedObjectList< T >.ItemByIndex(AIndex: Integer): T;
|
但是你有这行:
1
|
Result := TIndexedObject(nil);
|
这对于父类 TIndexedObjectList 来说很好,其中函数的结果是 TIndexedObject 类型,但对于子类 TCatalogueItemList 来说是不行的,其中函数的结果是类型 TCatalogueItem.
您可能知道,TCatalogueItem 实例是与 TIndexedObject 变量兼容的赋值,但反之则不然。它翻译成这样的:
1
2 3 |
function TCatalogueItemList.ItemByIndex(AIndex: Integer): TCatalogueItem;
begin Result := TIndexedObject(nil); //did you see the problem now? |
要将结果初始化为 nil 值,可以调用 Default() 伪函数,如下所示:
1
|
Result := Default(T);
|
在 Delphi XE 或更高版本中,该解决方案也是通用的。不是将结果类型转换为固定的 TIndexedObjectList 类,而是使用 T 类型
应用泛型类型转换
1
2 3 |
Result := T(nil);
//or Result := T(SomeOtherValue); |
但是,在这种特定情况下,不需要对 nil 常量进行类型转换,因为 nil 是一个与任何引用兼容的特殊值,因此您只需将行替换为:
1
|
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)。学到了一些新东西。
- @MarjanVenema 你没有注意。我们在这里每隔一周介绍一次Default(T)!
- @DavidHeffernan:哦,亲爱的,我被抓到了吗?一直忙于其他事情,不要像我一样关注SO…
来源:https://www.codenong.com/14992875/