为什么没有为子类生成接口表?

时间:2016-05-16 09:31:38

标签: delphi interface polymorphism

我有以下问题:

  • 实现特定接口的一个基类
  • 另一个从基类继承并覆盖接口方法的类

对于子类,根本不生成接口表。

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接口。但是一旦我们去运行时,我确实需要从基础重复声明接口!我们为什么要那样做?对我来说它看起来很麻烦。

2 个答案:

答案 0 :(得分:2)

记录在案GetInterfaceTable会返回该类的接口条目列表。在您的情况下,TChild没有任何接口本身实现。

  

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

     

GetInterfaceTable返回类的接口条目。这个   list仅包含此类实现的接口,而不包含它   祖先。要查找祖先列表,请迭代调用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

答案 1 :(得分:1)

它是按照设计的。

AFAIR <div> <div>Name: {{cus.name}}</div> <div> Age: {{cus.age}}</div> </div> 是一种非常低级的方法,仅适用于给定的类级别。您不必在代码中使用此方法,除非您正在处理低级RTTI信息......但在所有情况下,最好不要使用它。

这是如何实现的:

GetInterfaceTable

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

例如,下面是它在System.pas中的使用示例:

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

相当低级别的东西,不是吗?

要实现IoC模式,您宁愿使用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; ,这可以达到预期效果。