基本上,我希望一个类能够实现相同通用接口的两个不同版本.
考虑这段代码
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打算支持这个并简单地实现它错了吗?或者,他们从来没有打算让程序员做这样的事情吗? (老实说,我的测试程序甚至编译时有点惊讶)
解决方法
与接口类型一样,使用接口强制转换,它使用GUID查找接口.对于具有GUID的通用接口,每个实例都获得相同的GUID.如果单个类型实现接口的多个副本,则通过GUID查找将导致返回第一个接口.
如果您不使用接口强制转换,程序将按预期工作,而是使用如下所示的接口转换:
procedure Test; var t : TTest<integer,'Blah'); ShowMessage(ITest<string>(t).Val); ShowMessage(IntToStr(ITest<Integer>(t).Val)); end;
最初,当为Win32实现泛型时,通用接口上不允许使用GUID.但是,通用接口的动态查询对于通用容器方案是理想的,并且通常,作为在算法的上下文中查询服务提供者以获取特定于类型的服务的机制(例如,排序或搜索,其需要诸如比较器和平等测试).因此形成了一个新的计划:在通用接口上有一个GUID,但是为通用实例化创建类型参数的哈希,并将哈希折叠(例如xor)到GUID中,以便为每个不同且不兼容的实例化创建唯一的GUID.然而,这是在当天晚些时候,并且在时间限制内不可能实现良好的实施.但动态查询的要求仍然存在,因此GUID仍然存在.这就是为什么事情就像今天一样.