11.2.4 接口的多态性
在上一节中,我们看到了如何定义多个接口,并让一个类实现其中的两个接口。当然,这可以扩展到任何数量。您还可以创建接口的层次结构,因为一个接口可以继承另一个接口:
ITripleJumper = interface(IJumper)
[‘{0876F202-AAD3-11D2-8551-CCA30C584521}’]
function TripleJump: string;
end;
这个新接口类型拥有其基接口类型的所有方法(和属性),并增加了一个新方法。当然,接口的兼容规则与类的兼容规则大致相同。
不过本节中我想重点讨论的是一个稍有不同的话题,即基于接口的多态性。给定一个普通的基类对象,我们可以调用一个虚方法,并确保调用的是正确的版本。同样的情况也会发生在接口上。然而,对于接口,我们可以更进一步,经常使用动态代码来查询接口。鉴于对象与接口之间的关系可能相当复杂(一个对象可以实现多个接口,也可以间接地实现它们的基础接口),我们有必要更好地了解在这种情况下可能发生的事情。
首先,假设我们有一个通用的 IInterface 引用。我们如何知道它是否支持某个特定接口呢?实际上有多种方法,它们与类的对应方法略有不同:
-
使用 is 语句进行测试(也可能使用 as 语句进行后续转换)。这只能用来检查一个对象是否实现了某个接口,但不能用来检查一个接口是否实现了另一个接口(也就是说,不能将 is 应用于接口)。请注意,在任何情况下都需要进行 as 转换,因为直接转换为接口类型几乎都会导致错误。
-
调用全局 Support 函数,从众多的重载版本选择一个。你可以向该函数传递要测试的对象或接口、目标接口(使用 GUID 或类型名称),还可以传递一个接口变量,如果函数成功执行,实际接口将被存储在该接口变量中。
-
直接调用 IInterface 基接口的 QueryInterface ,该方法比较底层,总是需要接口类型变量作为额外结果,并使用数字 HRESULT 值而不是布尔结果。
下面是从同一个 IntfDemo 示例中截取的片段,展示了如何将后两种技术应用于通用 IInterface 变量:
procedure TForm1.Button4Click(Sender: TObject);
var
Intf: IInterface;
WalkIntf: IWalker;
begin
Intf := TAthlete.Create;
if Supports(Intf, IWalker, WalkIntf) then
Show(WalkIntf.Walk);
if Intf.QueryInterface(IWalker, WalkIntf) = S_OK then
Show(WalkIntf.Walk);
end;
与 QueryInterface 调用相比,我完全推荐使用 Supports 函数的一个重载版本。最终,Supports 会调用QueryInterface函数,但Supports 函数提供的选项更简单、更高级。
您想使用接口的多态性的另一个情况是,当您有一个高级接口类型的接口数组时(也可能是对象数组,其中一些对象可能支持给定接口)。