You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TOwnedCollection添加多类TCollectionItem子类报类型错误问题

问题根因

你的代码触发类型转换错误的核心原因有两点:

  • 自定义的TMyItems.Add方法默认只会创建基类TMyItem的实例,没有任何创建TMyItem2、TMyItem3子类实例的逻辑,对实际类型为基类的对象做as TMyItem2类型转换,必然抛出无效类型转换错误。
  • 你没有理解Indy组件中TIdMessageParts.Add返回nil的设计意图:这个写法是故意禁用无参数Add方法创建基类实例的能力,强制开发者显式指定要创建的集合项类型,避免生成不符合预期的基类对象。
修正方案

第一步:补充类引用声明,修改集合类Add方法

首先在TMyItem类定义前新增集合项的类引用类型,用于在Add方法中传入要创建的项类型,同时给集合类重载支持传入项类的Add方法:

// 声明集合项类引用,用于指定要创建的子类类型
TMyItemClass = class of 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
  // 无参Add和Indy逻辑一致,返回nil禁止直接创建基类
  function Add: TMyItem; reintroduce;
  // 重载Add,传入项类创建对应类型的实例
  function Add(ItemClass: TMyItemClass): TMyItem; reintroduce;
  function IndexOf(aFieldName: string): Integer;
  function MyItemByFieldName(aFieldName: string): TMyItem;
  property Items[aIndex: Integer]: TMyItem read GetMyItem; default;
end;

第二步:实现Add方法的逻辑

function TMyItems.Add: TMyItem;
begin
  Result := nil;
end;

function TMyItems.Add(ItemClass: TMyItemClass): TMyItem;
begin
  // 传入什么类就创建什么类的实例,自动归属到当前集合
  Result := ItemClass.Create(Self);
end;

第三步:修改调用代码

创建集合项时显式传入要创建的项类型,不需要再用as做类型强转:

// 创建基类TMyItem实例
with MyItems.Add(TMyItem) do
begin
  LabelName := 'test';
end;

// 创建TMyItem2实例
with TMyItem2(MyItems.Add(TMyItem2)) do
begin
  LabelName := 'label item2';
  Caption := 'caption item2';
end;

// 创建TMyItem3实例
with TMyItem3(MyItems.Add(TMyItem3)) do
begin
  LabelName := 'label item3';
  Caption3 := 'caption3 item3';
end;

说明:TOwnedCollection原生支持存储继承自集合项基类的所有子类实例,不需要额外做类型注册。你之前的错误和集合的多态支持能力无关,纯粹是创建的对象实际类型和你要转换的目标类型不匹配导致的。

内容的提问来源于stack exchange,提问作者Peter Jones

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 01:42:27