如何在Delphi的TInterfaceList中存储和定位多个接口类型

6

我将来自多个对象的小接口存储到单个TInterfaceList“存储”中,旨在为最终用户提供特定接口类型的列表,因此每个接口都将公开一个“GetName”函数,但所有其他方法都是唯一的。例如,这里有两个接口:

  IBase = interface
    //----------------------------------------
    function GetName : string;
    //----------------------------------------
  end;

  IMeasureTemperature = interface(IBase)
    //------------------------------------
    function MeasureTemperature : double;
    //----------------------------------------
  end;

  IMeasureHumidity = interface(IBase)
    //----------------------------------------
    function MeasureHumidity: double;
    //----------------------------------------
  end;

我将这些接口放入一个TInterfaceList中,然后我想扫描列表以查找特定的接口类型(例如“IMeasureTemperature”),并构建指向导出这些接口的对象的另一个指针列表。我希望不对这些对象的位置做任何假设,有些对象可能会导出多种类型的接口。我知道我可以使用类层次结构来完成此操作,例如:

  If FList[I] is TMeasureTemperature then ..

但我希望使用接口类型实现类似的功能,这种方式可行吗?


1
你可能需要澄清一下你的第一个列表是包含实际接口类型还是实现这些接口的对象实例的引用。目前为止,所有回答这里的人都假设后者,而我认为你的问题是在谈论前者...但是你不能将接口类型存储在TInterfaceList中...所以我可能是错的... - Oliver Giesen
3个回答

6
只需像这样使用Supports:
var
  oMTIntf: IMeasureTemperature;
...
  If Supports(FList[I], IMeasureTemperature, oMTIntf) then .. 

@da-soft:建议不错,但是当单个类导出两个不同的接口时会失败——在这种情况下,“Supports”对于每个接口都返回true。我需要一些方法来识别接口的实际类型。 - Brian Frost
抱歉,似乎我错过了您的重点。您是需要从接口获取对象,还是需要从接口获取接口?如果是前者,请参考http://www.malcolmgroves.com/blog/?p=500。如果是后者,则如我所写(我已编辑我的消息)。 - da-soft
@Brian:这有什么问题吗?如果两个接口中的一个是另一个的子类,那么在检查父类之前先检查子类。如果它们没有关系,那么两种结果都是有效的。 - Mason Wheeler
3
如果你在使用接口,那么具体的类是否同时实现这两个接口是无关紧要的。如果确实有影响,那可能就是设计上的缺陷信号。 - Lieven Keersmaekers
@Ken:没错。我想要一个解决方案来唯一标识出要向用户提供的确切接口类型,例如所有的“MeasureTemperature”类型,无论它们在哪里托管。 “Supports”解决方案(尽管很好)如果与“MeasureTemperature”托管在同一类上,则会返回“MeasureHumidity”的结果。我想解决方案是分离实现类,这样做会增加调用复杂性的痛苦。 - Brian Frost
显示剩余6条评论

2

您可以使用SysUtils中的Supports函数,它们非常安全(除非您在未初始化的内存上尝试使用它们),并且您只需要一个与您尝试转换的确切接口类型相匹配的目标变量:


procedure DoSomethingInList(AList: IInterfaceList;);
var
  i: Integer;
  liItem: IInterface;
  liMeasureTemp: IMeasureTemperature;
  liMeasureHumi: IMeasureHumidity;
begin
  AList.Lock;
  try
    for i := 0 to AList.Count - 1 do
    begin
      liItem := AList[i];
      if Supports(liItem, IMeasureTemperature, liMeasureTemp) then
        //... liMeasureTemp.MeasureTemperature ...
      else if Supports(liItem, IMeasureHumidity, liMeasureHumi) then
        //... liMeasureHumi.MeasureHumidity ...
      else 
        //...
    end;
  finally
    AList.Unlock;
  end;
end; 

2
我想这可能会满足您的需求。
function InterfaceRefIsInterface(Intf : IUnknown; ExpectedIntf : TGUID) : Boolean;
var vReference : IUnknown;
begin
  if Supports(Intf, ExpectedIntf, vReference)  then
    Result := Intf = vReference
  else
    Result := False;
end;

我不确定当Intf和ExpectedIntf互相继承时,该函数会如何表现,但如果Intf与ExpectedIntf完全匹配,则此函数将返回TRUE。

在您的示例中,IMeasureHumidity不会在IMeasureTemperature上返回true,但我不确定它对IBase会有什么反应。根据初步测试,它也会在IBase上返回FALSE。


除非 ExpectedIntfIUnknown,否则不能保证 Intf 将等于 vReference。实现 QueryInterface 的规则承诺调用 QueryInterface 将成功,但它们不要求返回任何特定的接口指针。请参阅 实现 QueryInterface 的规则 - Rob Kennedy
从您自己的链接中,“使用IID_IUnknown调用QueryInterface必须始终返回相同的物理指针值”,并且“任何接口都必须能够查询对象上的任何其他接口”。我不确定您所指的规则会导致此失败。 - Ken Bourassa
这个很好用,而且正好做到了我所希望的,尽管我需要仔细看看它在做什么!永远不会出现传递继承的IBase接口的情况。非常感谢。 - Brian Frost

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