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

foo*_*ser 19 c# inheritance interface-implementation

尝试为同一个类实现两次接口时遇到编译器错误,如下所示:

public class Mapper<T1, T2> : IMapper<T1, T2>, IMapper<T2, T1>
{
   /* implementation for IMapper<T1, T2> here.  */

   /* implementation for IMapper<T2, T1> here.  */
}
Run Code Online (Sandbox Code Playgroud)

错误:

'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. */
}
Run Code Online (Sandbox Code Playgroud)

编辑:我已经更新MyClass,MyClassBase以及IMyInterfaceMapper,MapperBase以及IMapper代表一个更真实的场景,这个问题可以提出自己.

Tim*_*lds 21

考虑这个实现:

public class MyClass<T1, T2> : IMyInterface<T1, T2>, IMyInterface<T2, T1>
{
   /* implementation for IMyInterface<T1, T2> here.  */

   /* implementation for IMyInterface<T2, T1> here.  */
}
Run Code Online (Sandbox Code Playgroud)

什么是MyClass<int, int>执行?它实现了IMyInterface<int, int>两次,因为IMyInterface<T1, T2>并且IMyInterface<T2, T1>统一时T1T2相等.这就是为什么既实现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. */
}
Run Code Online (Sandbox Code Playgroud)

你所做的是优先考虑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).如果要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();
    }
}
Run Code Online (Sandbox Code Playgroud)