含接口引用类的泛型子类调用崩溃原因解析求助
问题核心原因
你的代码中存在不安全的接口指针强制转换,这是导致AV的根本原因,而非Delphi的Bug:
接口实现的多指针特性:
TTest继承自TInterfacedObject(实现IInterface)同时实现了ITest接口。在Delphi中,一个类实现多个接口时,每个接口对应独立的vtable指针——IInterface指针指向TInterfacedObject的接口实现,ITest指针指向TTest自身的接口实现,两个指针地址不同。SetInterface调用的隐式转换:
调用继承的SetInterface(aTestInstance)时,参数类型是IInterface,Delphi会自动将TTest实例转换为IInterface接口指针(取TInterfacedObject实现的那个指针)并存储到FInterface。非法的强制转换触发AV:
TInterfaceContainerGenericDescendant.GetInterface中,你将IInterface指针强制转换为ITest。但这个IInterface指针并非ITest接口的实现指针,强制转换后调用GetValue会访问错误的vtable地址,直接触发Access Violation。
重写泛型SetInterface修复问题的原因
当你重写SetInterface为接受泛型I(即ITest)参数时:
- 传入
TTest实例时,Delphi会自动转换为ITest接口指针(而非IInterface),再传递给父类方法存储到FInterface。 - 此时
FInterface存储的是合法的ITest接口指针(它继承自IInterface,可以安全赋值给IInterface变量),后续强制转换为ITest时完全合法,调用方法自然不会出错。
结论
这是Delphi接口类型系统的特性,而非Bug。问题出在你对不同接口指针进行了不安全的强制转换,属于未定义行为。虽然现有代码大量使用接口转实现类的结构,但这里的核心问题是跨接口的非法转换,需要注意接口指针的类型匹配。
示例代码
type TInterfaceContainer = class FInterface: IInterface; procedure SetInterface(const AInterface: IInterface); function GetInterface: IInterface; end; TInterfaceContainerGenericDescendant<I: IInterface> = class(TInterfaceContainer) function GetInterface: I; //procedure SetInterface(const AInterface: I); //this fixes the crash, why ? end; ITest = interface function GetValue: Int64; procedure SetValue(AValue: Int64); end; TTest = class(TInterfacedObject, ITest) FValue: Int64; function GetValue: Int64; procedure SetValue(AValue: Int64); end; procedure Test; begin var aInterfaceContainer := TInterfaceContainerGenericDescendant<ITest>.Create; try var aTest: ITest := TTest.Create; aTest.SetValue(11); var aTestInstance := aTest as TTest; aInterfaceContainer.SetInterface(aTestInstance); var aValue := aTest.GetValue; aValue := aTestInstance.GetValue; aValue := aInterfaceContainer.GetInterface.GetValue; //AV on this line. Why ? finally aInterfaceContainer.Free end end; { TInterfaceContainer } function TInterfaceContainer.GetInterface: IInterface; begin result := FInterface end; procedure TInterfaceContainer.SetInterface(const AInterface: IInterface); begin FInterface := AInterface end; { TInterfaceContainerGenericDescendant<I> } function TInterfaceContainerGenericDescendant<I>.GetInterface: I; begin result := I(inherited GetInterface) end; //procedure TInterfaceContainerGenericDescendant<I>.SetInterface( const AInterface: I); //begin // inherited SetInterface(AInterface) //end; { TTest } function TTest.GetValue: Int64; begin result := FValue end; procedure TTest.SetValue(AValue: Int64); begin FValue := AValue end;
内容的提问来源于stack exchange,提问作者Peter

