接口实现两次“类型”可以统一.为什么这个解决方法有效?
本文关键字:为什么 解决 有效 方法 实现 两次 类型 接口 | 更新日期: 2023-09-27 17:54:51
当我试图为同一个类实现两次接口时,遇到了编译器错误,如下所示:
public class Mapper<T1, T2> : IMapper<T1, T2>, IMapper<T2, T1>
{
/* implementation for IMapper<T1, T2> here. */
/* implementation for IMapper<T2, T1> here. */
}
错误:
'Mapper'不能同时实现'IMapper'和'IMapper',因为它们可能会统一用于某些类型参数替换。
为什么这个解决方法有效?我想知道我是否已经解决了问题,或者只是欺骗了编译器。
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. */
}
EDIT:我已将MyClass
, MyClassBase
和IMyInterface
更新为Mapper
, MapperBase
和IMapper
,以代表更真实的场景,该问题可能会出现。
考虑这个实现:
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>
,因为当T1
和T2
相等时,IMyInterface<T1, T2>
和IMyInterface<T2, T1>
是统一的。这就是为什么在同一个类上同时实现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)
的行为。如果要在B<T1, T2>
上显式地实现I<T2, T1>
(通过在方法名前加上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();
}
}
您没有欺骗编译器,您这样做是为了使您不会有相互竞争的函数定义。假设您的接口具有string Convert(T1 t1, T2 t2)
函数。对于您的第一个(非法)代码,如果您执行MyClass<string, string>
,您将拥有相同函数的两个实例。使用您的基类,这2个实例将在MyClassBase
和MyClass
中,因此MyClass
中的一个将隐藏另一个,而不是与之冲突。
我认为这个问题是由于编译器无法显示如果T1
或T2
类型之一是另一个(或相同类型)的后代应该调用哪个实现的方法引起的。
想象一下,如果实例化类MyClass<int, int>
,它应该做什么。