我有以下问题:
对于子类,根本不生成接口表。
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 :(得分: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;
,这可以达到预期效果。