11.2.5 从接口引用中提取对象
在过去多个Object Pascal 语言版本中,当你将一个对象赋值给一个接口变量时,是无法访问原始对象的。有时,开发人员会在接口中添加一个 GetObject 方法来执行该操作,但这种设计非常奇怪。
在今天的Object Pascal语言中,你可以将接口引用转回先前被用来赋值的原始对象。你可以使用三种不同的操作:
-
你可以编写一个 is 测试语句来验证是否能从接口引用中提取出一个给定类型的对象:
IntfVar is TMyObject
-
您可以编写一个 as 转换来执行类型转换,如果出现错误,则会引发异常错误时引发异常:
IntfVar as TMyObject。
-
你可以编写一个强制类型转,如果出现错误,则返回一个 nil指针:
TMyObject(IntfVar)
注解 在任何情况下,只有在接口最初是从 Object Pascal 对象而不是 COM server获取的情况下,类型转换操作才有效。还要注意的是,不仅可以向原始对象的类进行类型转换,还可以向其任一基类进行类型转换(遵循类型兼容性规则)。
举例来说,可以看看一下这个简单的接口及其实现类(ObjFromIntf 示例的一部分):
type
ITestIntf = interface(IInterface)
['{2A77A244-DC85-46BE-B98E-A9392EF2A7A7}']
procedure DoSomething;
end;
TTestImpl = class(TInterfacedObject, ITestIntf)
public
procedure DoSomething;
procedure DoSomethingElse; // 不在接口中
destructor Destroy; override;
end;
有了这些定义,你现在就可以定义一个接口变量,为其分配一个对象、进行一个cast转换,用它来调用接口中没有的一个方法:
var
Intf: ITestIntf;
begin
Intf := TTestImpl.Create;
Intf.DoSomething;
(Intf as TTestImpl).DoSomethingElse;
您也可以用下面的方式编写代码,使用 is 测试语句和直接类型转换,而且您总是可以转换到对象实际类的基类:
var
Intf: ITestIntf;
Original: TObject;
begin
Intf := TTestImpl.Create;
Intf.DoSomething;
if Intf is TObject then
Original := TObject(Intf);
(Original as TTestImpl).DoSomethingElse;
考虑到直接转换如果失败会返回 nil,您也可以编写如下代码(不使用 is 测试):
Original := TObject(Intf);
if Assigned(Original) then
(Original as TTestImpl).DoSomethingElse;
请注意,将从接口中提取的对象赋值给变量会导致引用计数问题:当接口被设置为 nil 或退出作用域时,对象实际上已被删除,引用它的变量也将失效。您可以在示例的 BtnRefCountIssueClick 事件处理器中找到突出显示该问题的代码。