Home > Back-end >  TOwnedCollection and using multiple inherited TCollectionItem
TOwnedCollection and using multiple inherited TCollectionItem

Time:06-02

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;
  • Related