TOwnedCollection 和使用多个继承的 TCollectionItem

TOwnedCollection and using multiple inherited TCollectionItem

我在同一 TOwnedCollection.

中添加自己的 TCollectionItem classes(继承自 TCollectionItem)时遇到问题

我参考了 Indy 的 IdMessageParts.pas 作为推荐的 TIdMessagePart。所以我一定是遗漏了一些东西,因为在添加 TMyItem2.

时出现“无效的 class 类型”错误

我希望 MyItems: TOwnedCollection 能够存储 TMyItemTMyItem2TMyItem3。但是在添加 TMyItem2TMyItem3 时出现“无效类型转换”错误(只能接受 TMyItem)。

我是不是漏掉了什么?

TMyItem = class(TCollectionItem)
private
  FLabelName: string;
public
  constructor Create(Collection: TCollection); override;
  destructor Destroy; override;
published
  property LabelName: string read FLabelName write FLabelName;
end;

TMyItem2 = class(TMyItem)
private
  FCaption: string;
published
  property Caption: string read FCaption write FCaption;
end;

TMyItem3 = class(TMyItem)
private
  FCaption3: string;
published
  property Caption3: string read FCaption3 write FCaption3;
end;

TMyItems = class(TOwnedCollection)
private
  function GetMyItem(aIndex: Integer): TMyItem;
protected
  constructor Create(aOwner: TAsapListview);
  function GetOwner: TPersistent; override;
public
  function Add: TMyItem;
  function IndexOf(aFieldName: string): Integer;
  function MyItemByFieldName(aFieldName: string): TMyItem;
  property Items[aIndex: Integer]: TMyItem read GetMyItem; default;
end;

// NOTE: in idMessageParts.pas the add is defined this way
// which I don't quite understand
{
function TIdMessageParts.Add: TIdMessagePart;
begin
  // This helps prevent TIdMessagePart from being added
  Result := nil;
end;
}

// this is the calling code
with MyItems.Add do
begin
  LabelName := 'test';  // works
end;
// Error of invalid typecast occurred here
with MyItems.Add as TMyItem2 do  // typecast error here
begin
  LabelName := 'label item2';
  Caption := 'caption item2';
end;
with MyItems.Add as TMyItem3 do  // typecast error here
begin
  LabelName := 'label item2';
  Caption := 'caption item2';
  Caption3 := 'caption3 item2';
end;

继承的 TCollection.Add() 方法创建 TCollection 构造函数中指定的 class 类型的实例。所以 Add() 在处理多个 TCollectionItem 派生的 class 时没用,因为你不能那样添加多个 class。

此代码:

with MyItems.Add do
begin
 ...
end;

有效是因为您告诉 TMyItems 它的项目 class 类型是 TMyItem,因此这就是 Add() 创建的内容。

此代码:

with MyItems.Add as TMyItem2 do
...
with MyItems.Add as TMyItem3 do

同样的原因失败了。您告诉 TMyItems 创建 TMyItem 对象,因此您不能将它们转换为其他 class 类型。

Indy 的 TIdMessageParts 集合覆盖 Add() 以阻止用户以这种方式使用 Add()

要完成你想要的(以及 Indy 所做的),你需要调用 TCollectionItem 构造函数而不是使用 Add(),例如:

with TMyItem.Create(MyItems) do
begin
  LabelName := 'test';  // works
end;

with TMyItem2.Create(MyItems) do
begin
  LabelName := 'label item2';
  Caption := 'caption item2';
end;

with TMyItem3.Create(MyItems) do
begin
  LabelName := 'label item2';
  Caption := 'caption item2';
  Caption3 := 'caption3 item2';
end;