尝试为同一个类实现两次接口时遇到编译器错误,如下所示:
public class Mapper<T1, T2> : IMapper<T1, T2>, IMapper<T2, T1>
{
/* implementation for IMapper<T1, T2> here. */
/* implementation for IMapper<T2, T1> here. */
}
错误:
&#39;映射器&#39;无法同时实现“IMapper”和“IMapper”。和&#39; IMapper&#39;因为他们可能统一某些类型参数替换。
为什么这种解决方法有效?我想知道我是否解决了这个问题,或者只是欺骗了编译器。
public class Mapper<T1, T2> : MapperBase<T1, T2>, IMapper<T1, T2>
{
/* implementation for IMapper<T1, T2> here. */
}
public class MapperBase<T1, T2> : IMapper<T2, T1>
{
/* implementation for IMapper<T2, T1> here. */
}
编辑:我已将MyClass
,MyClassBase
和IMyInterface
更新为Mapper
,MapperBase
,以及IMapper
代表一个更真实的场景,可能会出现此问题。
答案 0 :(得分:21)
考虑这个实现:
public class MyClass<T1, T2> : IMyInterface<T1, T2>, IMyInterface<T2, T1>
{
/* implementation for IMyInterface<T1, T2> here. */
/* implementation for IMyInterface<T2, T1> here. */
}
MyClass<int, int>
实施了什么?它实施IMyInterface<int, int>
两次,因为IMyInterface<T1, T2>
和IMyInterface<T2, T1>
在T1
和T2
相等时统一。这就是为什么不允许在同一个类上同时实现IMyInterface<T1, T2>
和IMyInterface<T2, T1>
的原因。如果您尝试实施IMyInterface<int, T1>
和IMyInterface<T2, double>
,则会采用相同的推理:类型表达式统一为T1 = double, T2 = int
。
考虑这个实现:
public class MyClass<T1, T2> : MyClassBase<T1, T2>, IMyInterface<T1, T2>
{
/* implementation for IMyInterface<T1, T2> here. */
}
public class MyClassBase<T1, T2> : IMyInterface<T2, T1>
{
/* implementation for IMyInterface<T2, T1> here. */
}
您所做的就是IMyInterface<T1, T2>
优先于IMyInterface<T2, T1>
。如果T1
和T2
相等且您的实例为MyClass<T1, T2>
,则会选择IMyInterface<T1, T2>
实施。如果您有MyBaseClass<T1, T2>
的实例,则会选择IMyInterface<T2, T1>
实施。
这是一个向您展示行为的玩具程序。特别注意a_as_i.M(0, 1)
和a_as_b.M(0, 1)
的行为。如果您要在I<T2, T1>
上明确实现B<T1, T2>
(通过在方法名称前添加I<T2, T1>.
),则无法使用编译时语法调用它。反思是必要的。
interface I<T1, T2>
{
void M(T1 x, T2 y);
}
class A<T1, T2> : B<T1, T2>, I<T1, T2>
{
public void M(T1 x, T2 y)
{
Console.WriteLine("A: M({0}, {1})", x, y);
}
}
class B<T1, T2> : I<T2, T1>
{
public void M(T2 x, T1 y)
{
Console.WriteLine("B: M({0}, {1})", x, y);
}
}
class Program
{
static void Main(string[] args)
{
//Outputs "A: M(0, 1)"
var a = new A<int, int>();
a.M(0, 1);
//Outputs "B: M(0, 1)"
var b = new B<int, int>();
b.M(0, 1);
//Outputs "A: M(0, 1)" because I<T1, T2>
//takes precedence over I<T2, T1>
var a_as_i = a as I<int, int>;
a_as_i.M(0, 1);
//Outputs "B: M(0, 1)" despite being called on an instance of A
var a_as_b = a as B<int, int>;
a_as_b.M(0, 1);
Console.ReadLine();
}
}
答案 1 :(得分:1)
你没有欺骗编译器,你已经做到了这一点,所以你不会有竞争的功能定义。假设您的界面具有函数string Convert(T1 t1, T2 t2)
。使用您的第一个(非法)代码,如果您执行MyClass<string, string>
,您将拥有2个相同功能的实例。对于您的基类,这两个实例将位于MyClassBase
和MyClass
中,因此MyClass
中的实例将隐藏另一个实例,而不是与之冲突。我认为这是否有效取决于你。
答案 2 :(得分:0)
我认为问题是由于编译器无法显示如果T1
或T2
类型中的一个是另一个类型的后代(或者是相同类型),则应该调用哪些已实现的方法)。
想象一下,如果实例化类MyClass<int, int>
,它应该怎么做。