如何检查接口对象是否实现了另一个接口?

9

我有以下这些接口声明:

IListener = interface
  procedure HandleEvent(AEvent: TMyEvent);
end;

IExtendedListener = interface(IListener)
  ['{85A3456A-D5E5-4F37-ABDD-A75A7B3B494C}']  // required by GetInterface
  procedure HandleExtendedEvent(AExtendedEvent: TMyExtendedEvent);
end;

如果我有实现了IListener接口的某个东西的接口引用,我该如何检查它是否还实现了IExtendedListener接口?因为像if Listener is IExtendedListener then ...这样做是编译时错误,所以我使用下面的代码。

TSomeClass.Notify(AEvent: TMyExtendedEvent);
var
  Listener: IListener;
  ExtListener: IExtendedListener;
  Obj: TObject;
begin
  for Listener in FListeners do
  begin
    // works but smells funny
    Obj := Listener as TObject;
    Obj.GetInterface(IExtendedListener, ExtListener);
    if Assigned(ExtListener) then
      ExtNotifyee.HandleExtendedEvent(AEvent);
  end;
end;

2
请查看http://docwiki.embarcadero.com/Libraries/XE3/en/System.SysUtils.Supports。 - Sir Rufo
1
你的代码没有检查IExtendedListener是否继承自IListener,仅仅是通过IListener引用的实例也支持IExtendedListener。这并不意味着两个接口之间有任何继承关系。 - Marjan Venema
1
@MarjanVenema,我问这个问题是因为“如果Listener是IExtendedListener”,那么它将无法编译。 - Lawrence Barsanti
没问题,我只是想确保你和任何阅读这篇文章的人都明白代码并没有涉及继承,以避免任何可能的混淆。Supports 是检查和获取正确引用的最简单方法。而且你需要获取一个特定于精确接口的引用,因为接口引用不能像对象引用一样进行“硬”转换。接口继承与类继承完全不同。 - Marjan Venema
1
@JeroenWiertPluimers,我认为你错了。我向TListener添加了一个GUID,但无法使用is。不过如果可以的话会很好。 - Lawrence Barsanti
显示剩余4条评论
1个回答

21

您应该使用Sysutils.Supports来检查特殊接口。

uses
  SysUtils;

TSomeClass.Notify(AEvent: TMyExtendedEvent);
var
  Listener    : IListener;
  ExtListener : IExtendedListener;
begin
  for Listener in FListeners do
    if Supports( Listener, IExtendedListener, ExtListener ) then
      ExtListener.HandleExtendedEvent( AEvent );
end;

我正在清理问题的代码,但是忘记删除self的引用。 - Lawrence Barsanti
@LawrenceBarsanti 好的,我明白了,这不会影响问题的:o) - Sir Rufo
可能他们这样做是为了优化,但他们仍然应该支持接口的 "is" 和 "as"。他们可以在编译器中检查是否可以通过一些内部缓存来优化后面跟着 "is" 的 "as",以获得接口 "转换" 的结果(在检查期间完成,我猜这对于 COM 接口是必需的)。 - George Birbilis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接