2016-05-16 54 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 –

相關問題