2017-07-06 220 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中需要演员阵容,我怀疑它可能在西雅图发生了变化,但我只是从这里的评论中知道这一点,并且我的记忆可能很容易出错 –