Long story short: The following piece of code does not compile in Delphi 10.1 Berlin (Update 2).
interface
uses
System.Classes, System.SysUtils;
type
TTest = class(TObject)
public
function BuildComponent<T: TComponent>(const AComponentString: String): T;
end;
TSomeComponent = class(TComponent)
public
constructor Create(AOwner: TComponent; const AString: String); reintroduce;
end;
implementation
{ TTest }
function TTest.BuildComponent<T>(const AComponentString: String): T;
begin
if T = TSomeComponent then
Result := TSomeComponent.Create(nil, AComponentString)
else
Result := T.Create(nil);
end;
{ TSomeComponent }
constructor TSomeComponent.Create(AOwner: TComponent; const AString: String);
begin
inherited Create(AOwner);
end;
Several error messages are emitted from the compiler:
E2015: Operator not applicable to this operand type
on line if T = TSomeComponent then
and
E2010 Incompatible types - 'T' and 'TSomeComponent'
on line Result := TSomeComponent.Create(nil, AComponentString)
.
To circumvent these, I could cast TClass(T)
(for #1), as described in LU RD's answer here (despite it is said, that this bug has already been fixed in XE6), and T(TSomeComponent.Create(nil, AComponentString))
(for #2). Although, I feel uncomfortable using explicit type-casting.
Is there any better way? Shouldn't the compiler recognize, that T
is of type TComponent
because I constrained it explicitly?
At first, I tried to declare the generic function's implementation like it's interface:
function TTest.BuildComponent<T: TComponent>(const AComponentString: String): T;
But this ended up with the error
E2029: ',', ';' or '>' expected but ':' found
This does not compile in any version of Delphi that I have encountered. You need to do some casting to persuade the compiler to compile this:
function TTest.BuildComponent<T>(const AComponentString: String): T;
begin
if TClass(T) = TSomeComponent then
Result := T(TSomeComponent.Create(nil, AComponentString))
else
Result := T(TComponentClass(T).Create(nil));
end;
That said, I think that I might prefer:
if TClass(T).InheritsFrom(TSomeComponent) then
in place of that equality test.
Even then, trying to splice in a new constructor with different arguments, to a class based on a virtual constructor looks like a recipe for disaster to me.