泛型搞乱接口名称映射?

时间:2009-11-11 23:54:34

标签: delphi generics delphi-2010

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

考虑这段代码

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打算支持这个并简单地实现它错了吗?或者,他们从来没有打算让程序员做这样的事情吗? (老实说,我的测试程序甚至编译时有点惊讶)

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

2 个答案:

答案 0 :(得分: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,但是为通用实例化创建类型参数的哈希,并将哈希折叠(例如xor)到GUID中,以便为每个不同且不兼容的实例化创建唯一的GUID。然而,这是在当天晚些时候,并且在时间限制内不可能实现良好的实施。但动态查询的要求仍然存在,因此GUID仍然存在。这就是为什么事情就像今天一样。

要解决您的具体情况,我建议的最好方法是使用具有显式GUID的不同后代;或者使用不同的机制来查询界面。

答案 1 :(得分:3)

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

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