Steffen Binas
Steffen Binas

Reputation: 1488

TGenericClass<T> containing a TObjectList<T> doesn't compile

I'm trying to write a generic class which contains a generic TObjectList< T > which should contain only Elements of TItem.

uses
  Generics.Collections;

type
  TItem = class
  end;

  TGenericClass<T: TItem> = class
  public
    SimpleList: TList<T>; // This compiles
    ObjectList: TObjectList<T>; // This doesn't compile: Compiler complaints that "T is not a class type"
  end;

Is this a wrong syntax? BTW: TGenericClass< T: class> compiles, but then the Items in the List are not TItem anymore which is what I don't want.

Upvotes: 3

Views: 1329

Answers (3)

Cobus Kruger
Cobus Kruger

Reputation: 8615

I like GameCat's answer (gave it +1) for the description of class constraints.

I have a slight modification of your code that works. Note that since you gave a constraint to say that T must be a descendant of TItem, you can actually just declare ObjectList as TObjectList<TItem> - no need to use T here.

Alternatively, you could create a proxy of sorts. First, note GameCat's comment about fields being private.

type
  TGenericClass<T: TItem> = class
  private
    type
      D = class(TItem); // Proxy to get your T into and object list
  private
    SimpleList: TList<T>;
    ObjectList: TObjectList<D>; // Compiles now, but there is that type issue
  public
    procedure Add(Item: T); // No direct access to ObjectList
  end;

Add is an example of how to access the object list. As it turns out, you can pass Item to ObjectList.Add with no trouble whatsoever:

procedure TGenericClass<T>.Add(Item: T);
begin
  ObjectList.Add(Item);
end;

I think that may be a bug though, so to protect yourself against that getting fixed:

procedure TGenericClass<T>.Add(Item: T);
var
  Obj: TObject;
begin
  Obj := Item;
  ObjectList.Add(D(Obj));
end;

Given your scenario though, I'd say TObjectList should do just fine.

Upvotes: 3

Mason Wheeler
Mason Wheeler

Reputation: 84650

This is a known bug with the D2009 compiler. It will most likely be fixed soon, either in an update or hotfix for 2009, or in Delphi 2010 (Weaver) once it gets released. Until then, you need some sort of workaround, unfortunately. :(

Upvotes: 7

Toon Krijthe
Toon Krijthe

Reputation: 53476

Generic types can have several constraints:

  • A class name, the generic type must be of that class of a descendant of the class.
  • An interface name, the generic type must implement that interface.
  • 'class', the generic type must be a class (this can't be combined with a classname).
  • 'record', the generic type must be a record.
  • 'constructor', a bit vague, but you can create instances of the generic class type.

If you create a generic that uses other generics, you need to copy the constraints, else it won't work. In your case, TObjectList has the class constraint. This means, your T needs that constraint too.

Unfortunately this can't be combined with the named class constraint.

So I advice you to use an interface, these can be combined:

type
  IItem = interface end;
  TItem = class (TInterfacedObject, IItem) end;
  TGenericClass<T: class, IItem> = class
  private
    FSimpleList: TList<T>;
    FObjectList: TObjectList<T>;
  end;

Besides, you should make your fields private else anyone can change them.

Upvotes: 7

Related Questions