Generics and constructors

Generics and constructors

Fair enough, if you qualify the type to require a constructor

TMyClass = class
private
  property Inner: T;
public;
  procedure InnerCreate;
end;

you must have a parameterless constructor to instantiate T.

TMyClass.InnerCreate;
begin
   Inner := T.Create;
end;

but - If you qualify the type of T with an actual class such as TControl that have a public virtual constructor that takes an argument.

constructor TControl.Create(AOwner: TComponent); override;

You still cannot specify that constructor for T.

TMyClass = class
private
  property Inner: T;
public;
  procedure InnerCreate;
end;

TMyClass.InnerCreate;
begin
   Inner := T.Create(nil); //<-- [dcc32 Error] : E2029 ')' expected but 'NIL' found
end;

Why is this not valid?  The method is public, and the actual type of T has been specified.

Comments

  1. Because the constructor constraint means that you have to have a parameterless constructor.  This is something borrowed from C# where the class constraint does not mean T has to be a class but a reference type. But not all reference types have a parameterless constructor.  But in Delphi every class has one (the one from TObject).

    To make your code work you have to write this (and leave out the constructor constraint):

    procedure TMyClass.InnerCreate;
    begin
      TControl(fInner) := TControlClass(T).Create(nil);
    end;

    ReplyDelete
  2. If you don't wanna know what exactly T type is and wanna create it, you only can use parameterless constructor (T.create). Once you type cast T to exactly type, you can create it with actual parameters.  Constraint constructor must have class with default constructor, however, since TObject is the root, you always have one. So no error here, but complain you create with parameters.

    ReplyDelete
  3. Stefan Glienke - That creates an instance of the actual type passed as T and not the T qualifier type?

    ReplyDelete
  4. Lars Fosdal Yes but only because the constructor is virtual and you are casting to TControlClass.

    ReplyDelete
  5. Lars Fosdal It calls the constructor that is defined by TControl if you have TControlClass = class of TControl in your code (don't know if it is predefined, but maybe it is).
    As long as your TControl descendant overrides the constructor of TControl you should be ok.

    ReplyDelete
  6. Martin Wienold Even if it does not override the TControl constructor he is ok (well unless there is another contructor with different parameters of course). And yes, TControlClass is defined in Controls.pas.
    The technique used here is nothing special to generics and has worked way before generics were introduced into Delphi. If you have a class reference and have the constructor is virtual it calls the latest constructor in the inheritance chain. (in fact that is how Delphi instantiates components that are streamed from the dfm)

    ReplyDelete

Post a Comment