【发布时间】:2011-08-22 05:57:55
【问题描述】:
我偶然发现了在某些情况下从接口硬转换到类失败的情况。
考虑以下类型定义:
IDummy<T> = interface
end;
TMyRecord = record
Intf:IDummy<Byte>;
end;
TDummy = class(TInterfacedObject, IDummy<Byte>)
public
end;
IThisBreaksIt = IDummy<Byte>; // <== this line triggers the error
现在是使用类型的简单代码:
var
ARecord:TMyRecord;
Item:IDummy<Byte>;
ImplWorks,
ImplBroken:TDummy;
begin
ARecord.Intf:=TDummy.Create;
Item:=ARecord.Intf;
ImplWorks:=TDummy(Item);
ImplBroken:=TDummy(ARecord.Intf); // <== resulting instance is broken
end;
所以我正在做的是将接口引用存储在记录中。现在我想通过硬转换将其转换回实现类。
这里有一个问题:如果我为我的接口定义别名 (IThisBreaksIt = IDummy<Byte>),这将失败。注释掉这一行,ImplBroken 不再被破坏。在损坏的情况下ImplWorks和ImplBroken的地址不同;而Item 和ImplBroken 的地址现在是相同的。似乎负责硬铸造的自动魔法无法发挥作用。
其他发现:将 TDummy(ARecord.Intf) 替换为 ARecord.Intf as TDummy 可以解决此问题。
这让我有些头疼,因为它隐藏在一堆代码中,我没想到会出现这种行为。这正常吗?
为 Cosmin 编辑:
努力将接口转换为对象的示例。
在 XE 中测试:有效(StreamAdaptIntf 和 StreamAdaptImpl 的指针不同;断言成功) 2009年测试:失败(StreamAdaptIntf和StreamAdaptImpl的指针相同,Assertion失败)
uses ActiveX;
var
Stream:TStream;
StreamAdaptIntf:IStream;
StreamAdaptImpl:TStreamAdapter;
begin
Stream:=TMemoryStream.Create;
StreamAdaptIntf:=TStreamAdapter.Create(Stream, soOwned);
StreamAdaptImpl:=TStreamAdapter(StreamAdaptIntf);
Assert(Integer(StreamAdaptImpl) <> Integer(StreamAdaptIntf));
end;
【问题讨论】:
-
刚刚在Delphi XE中测试了示例代码;对于
StreamAdaptImpl := TStreamAdapter(StreamAdaptIntf),Delphi 编译器生成了一个call @SafeIntfAsClass。我删除了我的答案,因为我假设 Delphi 永远不会在硬演员中做“聪明的事情”是错误的。 -
@Cosmin:这给我们留下了一个问题,为什么 Delphi 在上述情况下不再聪明。现在我已经习惯了:)