2016-05-16 53 views
3

我有以下问题:为什么不为子类生成接口表?

  • 一个基类,它实现某个接口
  • 其从基类下降并覆盖接口方法

对于孩子类接口表另一类根本不生成。

type 
    ITest = interface 
    ['{69068A88-6712-40E0-B1E3-DA265F7428EA}'] 
    procedure Test; 
    end; 

    TBase = class(TInterfacedObject, ITest) 
    protected 
    procedure Test; virtual; 
    public 
    constructor Create; 
    end; 

    TChild = class(TBase) 
    protected 
    procedure Test; override; 
    end; 

constructor TBase.Create; 
begin 
    Assert(GetInterfaceTable <> nil); 
end; 

因此使用下面的结构时:

var 
    X: ITest; 
begin 
    X := TChild.Create; 
end; 

我得到的断言失败。

因此我知道我需要重新声明类声明中的接口来解决此问题。但是,这是一个语言功能或编译器老问题?

因为在编译时编译器知道TChild正在实现ITest接口。但是一旦我们进入运行时,我需要从基地重复声明接口!我们为什么要这样做?对我来说,它看起来越野车。

+0

David Heffernan会告诉你,声明一个接口方法是虚拟的,并在派生类中重写它与接口无关。你可以做到但没用。如果您需要更改派生类中的接口方法,则应重新声明派生类中的接口,并且更改的方法可以是静态的 - 无需使其变为虚拟。 – kludg

+0

这是有道理的速度的方式,但不是在我的情况。所有类共享相同的接口,并且有些类可以覆盖这些方法。但无论如何,问题解决了 - 它是在IoC容器中,并解析父类所做的事情。 –

回答

2

如文档GetInterfaceTable返回该类的接口条目列表。在你的情况下,它是没有任何接口自己实现的TChild

返回指向包含由给定类实现的所有接口 的结构的指针。

GetInterfaceTable返回该类的接口条目。这个 列表仅包含由这个类实现的接口,而不是它的 祖先。要查找祖先列表,请迭代调用ClassParent和 ,然后对其返回的值调用GetInterfaceTable。要查找特定接口的条目 ,请改为使用GetInterfaceEntry方法。

GetInterfaceTable是类函数,就像ClassName是类函数一样。它取决于实例类别,而不是来自您称之为代码的哪一部分:

如果您运行以下代码,它会给你不同的ClassName,而不管你在调用TBase构造函数中的代码。

constructor TBase.Create; 
begin 
    writeln(ClassName); 
end; 

var 
    x : ITest; 

    X := TBase.Create; // outputs TBase 
    X := TChild.Create; // outputs TChild 
+0

感谢您的回答。这就是我的想法,现在我会确信:) –

1

它是按设计。

AFAIR GetInterfaceTable是一个非常低级的方法,它只能在给定的类级别上工作。你不应该在你的代码中使用这种方法,除非你搞乱了底层的RTTI信息...但是在任何情况下,你最好不要使用它。

这里它是如何实现的:

class function TObject.GetInterfaceTable: PInterfaceTable; 
begin 
    Result := PPointer(PByte(Self) + vmtIntfTable)^; 
end; 

所以,你必须检查父类的类型,也使用递归调用或循环。

举例来说,这里是其使用的System.pas样本:

class function TObject.InitInstance(Instance: Pointer): TObject; 
var 
    IntfTable: PInterfaceTable; 
    ClassPtr: TClass; 
    I: Integer; 
begin 
    FillChar(Instance^, InstanceSize, 0); 
    PPointer(Instance)^ := Pointer(Self); 
    ClassPtr := Self; 
    while ClassPtr <> nil do 
    begin 
    IntfTable := ClassPtr.GetInterfaceTable; 
    if IntfTable <> nil then 
     for I := 0 to IntfTable.EntryCount-1 do 
     with IntfTable.Entries[I] do 
     begin 
      if VTable <> nil then 
      PPointer(@PByte(Instance)[IOffset])^ := VTable; 
     end; 
    ClassPtr := ClassPtr.ClassParent; 
    end; 
    Result := Instance; 
end; 

相当低层次的东西,不是吗?

要实现IoC模式,您宁可使用TObject.GetInterfaceEntry(),它符合您的期望。

+0

这是用于IoC容器。 –

+0

它调用Supports()..时可能我只需要然后通过ClassParent.GetInterfaceTable ... –

+0

看看我们如何在mORMot.pas中实现TServiceContainerServer.AddImplementation - https://github.com/synopse/mORMot /blob/master/SQLite3/mORMot.pas –

相关问题