2017-07-06 221 views
3

有人可以解釋爲什麼在下面的代碼,class1List不要求class1的有一個參數的構造函數,但class2list 確實需要等級2,有一個參數的構造函數。泛型無參數構造函數

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. 

回答

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

class1構造函數聲明virtual。因此,編譯器知道T.Create產生一個實例T,它的預期構造函數已被調用。因此編譯器接受這個代碼。需要注意的是早期版本的編譯器會拒絕這個代碼,並迫使你使用下面的投

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

但更多的編譯器的最新版本已經取消了這種掛羊頭賣狗肉的需求。


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

class2構造函數是不是virtual,因此編譯器知道是它調用class2構造,有可能的類將不能正確初始化。它準備從專用類型T(如果存在)調用無參數構造函數,並且在聲明泛型類型時應用constructor約束。但是,該語言沒有辦法爲接受參數的構造函數應用構造函數約束。

現在,您可以應用constructor約束條件,但那樣做沒有用。爲了正確地初始化實例,您需要使用參數調用構造函數。實際上,這意味着您應該使用虛擬構造函數的第一種方法。

不要試圖走出這個洞。此代碼將編譯

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

但可能不會做你想做的。這將調用class2的靜態構造函數,這肯定不是你想要的。

+0

有趣。謝謝你的解釋。 – Dsm

+0

'請注意早期版本的編譯器'你會碰巧知道在哪個版本中修復了? – Johan

+0

@Johan在XE7中需要演員陣容,我懷疑它可能在西雅圖發生了變化,但我只是從這裏的評論中知道這一點,並且我的記憶可能很容易出錯 –