Jerry Dodge
Jerry Dodge

Reputation: 27296

How to use TObjectList for arbitrary class type?

I'm still a bit fuzzy with generics in Delphi, but have been using TObjectList<> quite widely. Now I have a situation where I have a base class with such a private field, but needs to be created for an arbitrary class, also inherited from another base.

To clarify, I have two base classes:

type
  TItem = class;
  TItems = class;

  TItemClass = class of TItem;

  TItem = class(TPersistent)
  private
    FSomeStuffForAllIneritedClasses: TSomeStuff;
  end;

  TItems = class(TPersistent)
  private
    FItems: TObjectList<TItem>;
    FItemClass: TItemClass;
  public
    constructor Create(AItemClass: TItemClass);
    destructor Destroy; override;
    function Add: TItem;
    ...
  end;

This pair of classes is then further inherited into more specific classes. I'd like the object list to be shared for all of them, while each holds actually a different type internally.

type
  TSomeItem = class(TItem)
  private
    FSomeOtherStuff: TSomeOtherStuff;
    ...
  end;

  TSomeItems = class(TItems)
  public
    function Add: TSomeItem; //Calls inherited, similar to a TCollection
    procedure DoSomethingOnlyThisClassShouldDo;
    ...
  end;

Now the problem is when it comes to creating the actual object list. I'm trying to do it like this:

constructor TItems.Create(AItemClass: TItemClass);
begin
  inherited Create;
  FItemClass:= AItemClass;
  FItems:= TObjectList<AItemClass>.Create(True);
end;

However, the code insight complains about this:

Undeclared Identifier AItemClass

Even more, the compiler has yet a different complaint:

Undeclared Identifier TObjectList

Where, I do in fact have System.Generics.Collections used in this unit.

What am I doing wrong here, and how should I do this instead?

Upvotes: 1

Views: 851

Answers (2)

Stefan Glienke
Stefan Glienke

Reputation: 21758

Make TItems generic:

TItems<T: TItem, constructor> = class(TPersistent)
private
  FItems: TObjectList<T>;
public
  constructor Create;
  destructor Destroy; override;
  function Add: T;
  ...
end;

constructor TItems.Create;
begin
  inherited Create;
  FItems:= TObjectList<T>.Create(True);
end;

function TItems<T>.Add: T;
begin
  Result := T.Create;
  FItems.Add(Result);
end;

If you inherit, simply put the correct generic parameter:

TSomeItems = class(TItems<TSomeItem>)
public
  procedure DoSomethingOnlyThisClassShouldDo;
  ...
end;

Upvotes: 6

Jerry Dodge
Jerry Dodge

Reputation: 27296

The TObjectList is not meant to be used in that manner. The fact that it was originally defined as TObjectList<TItem> means that it will expect you to create it this way as well. It needs to be defined with the precise class you intend to create it as.

Instead, just create it with TItem, and then whenever you create a new item which is supposed to be added to this list, then you create it using the class type. Any time you need to access the items in this list, just cast them on the fly.

For example...

Result:= FItemClass.Create;
FItems.Add(Result);

...can be the contents of your Add function.

Upvotes: 2

Related Questions