2009-11-11 81 views
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显示“胡说”为我所期望的,但第二次崩溃。它崩溃的原因是因为调用调用T2Val()而不是Val(),正如我所预料的那样。显然,冲突解决映射为两种类型的接口映射方法,而不仅仅是针对ITest:T2。

所以,这是我的问题。

这是一个错误?我的意思是,Embarcadero打算如何支持它,并简单地实施它是错误的?还是他们从来没有任何意图让程序员做这样的事情呢? (老实说,我有点惊讶,我的测试程序甚至编译过)

如果这是一个错误,有没有人有任何想法,接口?

回答

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依然存在。这就是为什么事情是今天的样子。

为了解决你的具体情况,我可以推荐的最好方法是使用明确的GUID来区分不同的后代;或者使用不同的机制来查询界面。

+0

呵呵。很高兴知道。谢谢。你只是巴里信息的金矿。感谢您花时间。 – TrespassersW 2009-11-12 05:39:26

+0

D2010已经这个哈希了,巴里? – 2009-11-12 13:31:25

+0

不幸的是,它不。我真的应该看看它。 – 2009-11-12 15:06:45

3

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

这似乎是一个错误,所以应该通过质量中心报告。