2

Longue histoire courte: Le morceau de code suivant ne compile pas dans Delphi 10.1 Berlin (mise à jour 2).Impossible de compiler la méthode générique contrainte

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; 

Plusieurs messages d'erreur sont émis par le compilateur:

  1. E2015: Opérateur non applicable à ce type d'opérande

    en ligne if T = TSomeComponent then et

  2. E2010 types incompatibles - 'T' et 'TSomeComponent'

    en ligne Result := TSomeComponent.Create(nil, AComponentString).

Pour contourner ceux-ci, je pourrais jeter TClass(T) (pour n ° 1), comme décrit dans LU RD's answer here (malgré il est dit, que ce bug a déjà été corrigé dans XE6) et T(TSomeComponent.Create(nil, AComponentString)) (pour # 2). Bien que, je me sens mal à l'aise en utilisant le moulage de type explicite.

Y a-t-il un meilleur moyen? Est-ce que le compilateur ne devrait pas reconnaître que T est de type TComponent parce que je l'ai contraint explicitement?


Au début, j'ai essayé de déclarer la mise en œuvre de la fonction générique comme son interface:

function TTest.BuildComponent<T: TComponent>(const AComponentString: String): T; 

Mais cela a fini avec l'erreur

E2029: », '' " ou '>' attendu mais ':' trouvé

+1

"Le compilateur ne devrait-il pas reconnaître que T est de type TComponent parce que je l'ai contraint explicitement?" Non, ça ne va pas. Une contrainte générique ne résout pas le type. Il aide simplement le compilateur * à empêcher * d'utiliser le type ou la procédure générique avec des types autres que ceux de la contrainte. Voir ma réponse: https://stackoverflow.com/questions/43679740/test-if-an-interface-equals-a-type-parameter/43681952#43681952 –

Répondre

3

Ceci ne compile pas dans les versions de Delphi que j'ai rencontrées. Vous devez faire un peu de casting pour convaincre le compilateur de compiler ceci:

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; 

Cela dit, je pense que je pourrais préférer:

if TClass(T).InheritsFrom(TSomeComponent) then 

en place de ce test d'égalité.

Même alors, essayer de fusionner dans un nouveau constructeur avec des arguments différents, à une classe basée sur un constructeur virtuel ressemble à une recette pour un désastre pour moi.

+0

Je préférerais aussi le test 'InheritsFrom'. Et en effet, je ne donnerais pas au nouveau composant un constructeur différent.J'utiliserais des propriétés à la place. Et puis, le BuildComponent n'aurait probablement plus besoin d'être générique, il pourrait juste passer un TComponentClass (et peut-être un propriétaire). Ou peut-être qu'il pourrait faire complètement sans. –