接口实现两次“类型”可以统一.为什么这个解决方法有效?

本文关键字:为什么 解决 有效 方法 实现 两次 类型 接口 | 更新日期: 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, MyClassBaseIMyInterface更新为Mapper, MapperBaseIMapper,以代表更真实的场景,该问题可能会出现。

接口实现两次“类型”可以统一.为什么这个解决方法有效?

考虑这个实现:

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>,因为当T1T2相等时,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>。如果T1T2相等,并且您有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个实例将在MyClassBaseMyClass中,因此MyClass中的一个将隐藏另一个,而不是与之冲突。

我认为这个问题是由于编译器无法显示如果T1T2类型之一是另一个(或相同类型)的后代应该调用哪个实现的方法引起的。
想象一下,如果实例化类MyClass<int, int>,它应该做什么。

相关文章: