Can someone explain why in the code below, class1List does not require class1 to have a parameterless constructor, but class2list does require class 2 to have a parameterless constructor.
unit Unit11;
interface
uses
System.Generics.Collections;
type
class1 = class
public
constructor Create( const i : integer ); virtual;
end;
class1List<T : class1 > = class( TObjectList< T > )
public
function AddChild( const i : integer ) : T;
end;
class2 = class
public
constructor Create( const i : integer );
end;
class2List<T : class2 > = class( TObjectList< T > )
public
function AddChild( const i : integer ) : T;
end;
implementation
{ class1List<T> }
function class1List<T>.AddChild(const i: integer): T;
begin
Result := T.Create( i );
inherited Add( Result );
end;
{ class2List<T> }
function class2List<T>.AddChild(const i: integer): T;
begin
Result := T.Create( i );
inherited Add( Result );
end;
{ class1 }
constructor class1.Create(const i: integer);
begin
end;
{ class2 }
constructor class2.Create(const i: integer);
begin
end;
end.
function class1List<T>.AddChild(const i: integer): T;
begin
Result := T.Create( i );
inherited Add( Result );
end;
The constructor of class1
is declared virtual
. Therefore the compiler knows that T.Create
yields an instance of T
whose intended constructor has been called. Hence the compiler accepts this code. Note that earlier versions of the compiler would reject this code and force you to use the following cast
Result := T(class1(T).Create( i ));
But more recent versions of the compiler have removed the need for such trickery.
function class2List<T>.AddChild(const i: integer): T;
begin
Result := T.Create( i );
inherited Add( Result );
end;
The constructor of class2
is not virtual
and so the compiler knows that were it to call the constructor of class2
, likely the class would not be properly initialised. It is prepared to call a parameterless constructor from the specialised type T
if one exists, and you apply the constructor
constraint when you declare the generic type. However, the language offers no way to apply a constructor constraint for constructors that accept parameters.
Now, you could apply the constructor
constraint, but that would do no good. In order for the instance to be initialised properly, you need to call the constructor with the parameter. Which means, in practical terms, that you should use the first approach using a virtual constructor.
Don't be tempted to cast your way out of this hole. This code will compile
Result := T(class2(T).Create( i ));
but will likely not do what you want. This will call the static constructor of class2
which is surely not what you want.