I'm having problems adding my own TCollectionItem
classes (inherited from TCollectionItem
) within the same TOwnedCollection
.
I referred to Indy's IdMessageParts.pas
for TIdMessagePart
as recommended. So I must be missing something as I get a "Invalid class type" error when adding TMyItem2
.
I would like MyItems: TOwnedCollection
to be able to store TMyItem
, TMyItem2
, TMyItem3
. But I get an "Invalid typecast" error when adding TMyItem2
and TMyItem3
(only TMyItem
can be accepted).
Did I miss something?
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;
CodePudding user response:
The inherited TCollection.Add()
method creates an instance of the class type that is specified in the TCollection
constructor. So Add()
is useless when dealing with multiple TCollectionItem
-derived classes, since you can't add multiple classes that way.
This code:
with MyItems.Add do
begin
...
end;
Works because you told TMyItems
that its item class type is TMyItem
, so that is what Add()
creates.
This code:
with MyItems.Add as TMyItem2 do
...
with MyItems.Add as TMyItem3 do
Fails for the same reason. You told TMyItems
to create TMyItem
objects, so you can't cast them to your other class types.
Indy's TIdMessageParts
collection overrides Add()
to dissuade users from using Add()
in this manner.
To accomplish what you want (and what Indy does), you need to call the TCollectionItem
constructor instead of using Add()
, eg:
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;