6

基本上,我希望一个类能够实现同一个通用接口的两个不同版本。

考虑这段代码

type
  // a generic interface
  ITest<T> = interface
    ['{6901FE04-8FCC-4181-9E92-85B73264B5DA}']
    function Val: T;
  end;

  // a class that purports to implement two different types of that interface
  TTest<T1, T2> = class(TInterfacedObject, ITest<T1>, ITest<T2>)
  protected
    fV1: T1;
    fV2: T2;
  public
    constructor Create(aV1: T1; aV2: T2);
    function Val: T1;               // Val() for ITest<T1>
    function T2Val: T2;             // Val() for ITest<T2>
    function ITest<T2>.Val = T2Val; // mapping
  end;

constructor TTest<T1, T2>.Create(aV1: T1; aV2: T2);
begin
  inherited Create;
  fV1 := aV1;
  fV2 := aV2;
end;

function TTest<T1, T2>.T2Val: T2;
begin
  result := fV2;
end;

function TTest<T1, T2>.Val: T1;
begin
  result := fV1;
end;

/////////////
procedure Test;
var
  t : TTest<integer, string>;
begin
  t := TTest<integer, string>.Create(39, 'Blah');
  ShowMessage((t as ITest<string>).Val);            // this works as expected
  ShowMessage(IntToStr((t as ITest<integer>).Val)); // this gets AV
end;

如我所料,第一个 ShowMessage 显示“Blah”,但第二个崩溃。它崩溃的原因是因为调用调用 T2Val() 而不是 Val() 正如我所期望的那样。显然,冲突解决映射映射了两种类型的接口的方法,而不仅仅是 ITest: T2。

所以,这是我的问题。

这是一个错误吗?我的意思是,Embarcadero 是否打算支持这一点并且只是错误地实施它?还是他们根本就没有打算让程序员做这样的事情?(老实说,我的测试程序竟然编译了,我有点惊讶)

如果这是一个错误,是否有人知道是否有一种解决方法可以让我让一个类支持两种不同类型的单个通用接口?

4

2 回答 2

11

as使用接口类型使用接口转换,它使用 GUID 来查找接口。对于具有 GUID 的通用接口,每个实例化都获得相同的 GUID。如果单个类型实现了接口的多个副本,则通过 GUID 查找将导致返回第一个接口。

如果您不使用接口转换,程序将按预期工作,而是使用如下所示的接口转换:

procedure Test;
var
  t : TTest<integer, string>;
begin
  t := TTest<integer, string>.Create(39, 'Blah');
  ShowMessage(ITest<string>(t).Val);
  ShowMessage(IntToStr(ITest<Integer>(t).Val));
end;

最初,当为 Win32 实现泛型时,不允许在泛型接口上使用 GUID。然而,通用接口的动态查询对于通用容器场景来说是可取的,并且通常作为一种在算法上下文中查询服务提供者以获取特定类型服务的机制(例如排序或搜索,这需要比较器和平等测试)。所以形成了一个新的计划:在通用接口上有一个 GUID,但是为通用实例创建一个类型参数的散列,并将散列折叠(例如异或)到 GUID 中,以便为每个不同且不兼容的实例创建一个唯一的 GUID。然而,这已经很晚了,在时间限制内不可能有好的实施。但是动态查询的需求仍然存在,所以 GUID 保留了下来。那'

To solve your specific scenario, the best I can recommend is to use distinct descendants with explicit GUIDs; or use a different mechanism for querying for the interface.

于 2009-11-12T01:07:48.553 回答
3

这是一个有趣的问题。似乎正在发生的事情是编译器总是将接口映射到接口的最后一个指定版本(如果交换顺序,那么它会调用另一个方法)。这可能与两个接口具有相同的 GUID 签名这一事实有关,因此调度程序在看到接口调用时对应该调用哪个方法感到困惑。

这似乎是一个错误,因此应通过质量中心报告。

于 2009-11-12T00:44:22.343 回答