2017-07-06 3 views
3

quelqu'un sans paramètre peut-il expliquer pourquoi dans le code ci-dessous, class1List ne pas besoin Class1 d'avoir un constructeur sans paramètre, mais class2list ne besoin de classe 2 d'avoir un constructeur sans paramètre.Génériques sans constructeurs de

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. 

Répondre

4
function class1List<T>.AddChild(const i: integer): T; 
begin 
    Result := T.Create(i); 
    inherited Add(Result); 
end; 

Le constructeur de class1 est déclaré virtual. Par conséquent le compilateur sait que T.Create donne une instance de T dont le constructeur prévu a été appelé. Par conséquent, le compilateur accepte ce code. Notez que les versions antérieures du compilateur rejetterait ce code et vous obligent à utiliser les éléments suivants fonte

Result := T(class1(T).Create(i)); 

Mais les versions les plus récentes du compilateur ont supprimé la nécessité d'une telle supercherie.


function class2List<T>.AddChild(const i: integer): T; 
begin 
    Result := T.Create(i); 
    inherited Add(Result); 
end; 

Le constructeur de class2 n'est pas virtual et donc le compilateur sait qui étaient pour appeler le constructeur de class2, probablement la classe ne serait pas correctement initialisés. Il est prêt à appeler un constructeur sans paramètre du type spécialisé T s'il en existe un, et vous appliquez la contrainte constructor lorsque vous déclarez le type générique. Cependant, le langage n'offre aucun moyen d'appliquer une contrainte de constructeur aux constructeurs qui acceptent les paramètres.

Maintenant, vous pouvez appliquer la contrainte constructor, mais cela ne fonctionnera pas. Pour que l'instance soit initialisée correctement, vous devez appeler le constructeur avec le paramètre. Ce qui signifie, en termes pratiques, que vous devriez utiliser la première approche en utilisant un constructeur virtuel.

Ne soyez pas tenté de sortir de ce trou. Ce code compilera

Result := T(class2(T).Create(i)); 

mais ne fera probablement pas ce que vous voulez. Cela appellera le constructeur statique de class2 qui n'est sûrement pas ce que vous voulez.

+0

Intéressant. Merci pour votre explication. – Dsm

+0

'Notez que les versions antérieures du compilateur' sauriez-vous dans quelle version cela a été corrigé? – Johan

+0

@Johan La distribution est nécessaire dans XE7, je soupçonne qu'il pourrait avoir changé à Seattle, mais je ne sais que des commentaires ici, et ma mémoire pourrait facilement se tromper –