-1

I have a Delphi class that holds a generic like so

type
  TObjectList<T:class, constructor> = class
  private
    FList: Tlist<T>;
  public
    function CreateItem : T;
    function Next : T;
  .
  .
  .
  end;

In another part of my project, I need use TObjectList without T,like that

type
  NavControl = class
    _plist:Pointer;
  public 
    constructor Create<T:class,constructor>(list:TObjectList<T>);
  end

How I can affect list to _plist, and how I can call CreateItem, Next from _plist?

Johan
  • 74,508
  • 24
  • 191
  • 319
web master
  • 27
  • 6
  • Why is `_plist` an untyped `Pointer`? It needs to be a `TObjetcList` instead, which will require moving the Generic arguments from `Create` to `NavControl`. If you make just `Create` a Generic, you lose the ability to type-cast `_plist` later, because you don't know what `T` was passed to `Create`. – Remy Lebeau Apr 28 '17 at 01:04
  • NavControl is singlton???, for that raison, i can't use generic, all my lists need to be controled(adding, removing,...) from just one place with of corse witch active used list. – web master Apr 28 '17 at 20:06
  • Nothing in the code shown limits `NavControl` to a singleton. But even if it is a singleton, that doesn't change what I said. If you leave `_plist` as a `Pointer`, you have to type-cast it whenever you want to access its members, and you can't type-cast it if you don't know what type it is, and you can't use the `T` passed to `Create` since it only exists for `Create`. You need to re-think your approach. And especially if `NavControl` is a singleton, there is no reason why you can't move the `T` argument to `NavControl` instead of `Create`, then you can give `_plist` a proper type. – Remy Lebeau Apr 28 '17 at 20:16

1 Answers1

2

If you want to work with a generic type, you need to store the type of generic you're working with somewhere.

There is no type that can hold a type parameter however. You'll either have to store the type of T in the class itself, or figure out some other way of doing things.

You have two options:

Recommended option
Redeclare NavControl like so:

type
  TNavControl<T:class, constructor> = class
  private
    _Plist: TObjectList<T>;
  public
     constructor Create(const List: TObjectList<T>);

Creating new instances of T in your NavControl is easy:

begin
  NewItem:= T.Create;
  .....

Mishmash of generic and old-skool -not recommended-
The other way is to recognise that T is an object, as such you can store its class in a variable of type TClass, however in order to do so you'll have to get a little creative in your constructor.

type
  TNavControl = class
  private
    _PList: TObjectList<TObject>;
    ClassOfT: TClass;

constructor TNavControl.Create<T:class, constructor>(const List: TObjectList<T>);
var
  Dummy: T;
begin
  Dummy:= T.Create;
  try
    ClassOfT:= TObject(Dummy).ClassType;
  finally
    Dummy.Free;
  end;
end;

You'll now have to work with the TClass reference in order to do instantiate objects, but TClass requires virtual constructors which the generic constraint does not -Oops-.

My recommendation
Personally I would not bother with generics here.
You know you want a class and you know you need a constructor.
Using this method allows you to use a constructor with or without parameters, as long as the constructor is virtual.
Just limit your object list to a reasonable subclass that you control and use that.

type

  TMyClass = class(TControl)
    ...
    constructor Create(const param: string = ''); virtual; <<!!!!
  end;
  TMyClass2 = class(TMyClass)
    constructor Create(const param: string = ''); override <<!!
  end;

  TMyClassClass = class of TMyClass;

  TNavControl = class
  private
    FMyClassClass = TMyClassClass;
    FPList = TObjectList;  //non-generic version
  public
    constructor Create(const ClassType: TMyClassClass); 

  function TNavControl.NewItem(const param: string = ''): TMyClass;
  begin
    Result:= FMyClassClass.Create(param);  //Will create a TMyClass2 or 3
    //Or whatever type you fed into the constructor.
  end;

Now if you call

 var
   A: TMyClass2;
   B: TMyClass;
   C: TNavControl;

 A:= TMyClass.Create('test');
 C:= TNavControl.Create(A.ClassType);
 B:= C.NewItem('test2');   //Creates a TMyClass2

Generics really don't have much use when working with classes, better to use the class of ... and virtual constructors.
This also allows the use of constructors with parameters, which generic solutions do not cater for.
Just make sure to keep the constructor parameters the same throughout.

See this answer for more info: https://stackoverflow.com/a/758715/650492

Community
  • 1
  • 1
Johan
  • 74,508
  • 24
  • 191
  • 319
  • I need to avoid to set TNavControl as generic as possible, other part , interfaces seem to be perfect choise (TObjectList implement some interface) and all my code call TNavControl(is singlton) with that interface. sorry for not well descibing my problem. – web master Apr 28 '17 at 20:01
  • @webmaster: then please [edit] your question to show how you want to use `NavControl` so someone can show you how to do it properly, or a better way to do it. – Remy Lebeau Apr 28 '17 at 20:14