21

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

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. */
}

编辑:我已经更新了MyClass,MyClassBaseIMyInterfaceto Mapper, MapperBase, andIMapper来代表这个问题可能会出现的更真实的场景。

4

3 回答 3

22

考虑这个实现:

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>统一时T1T2相等。这就是为什么不允许在同一个类上实现两者的原因IMyInterface<T1, T2>IMyInterface<T2, T1>例如,如果您尝试实现IMyInterface<int, T1>and ,同样的推理也适用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>over 上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();
    }
}
于 2014-03-28T19:54:49.333 回答
1

你没有欺骗编译器,你已经做到了,这样你就不会有竞争的函数定义。假设您的界面具有功能string Convert(T1 t1, T2 t2)。使用您的第一个(非法)代码,如果您这样做了MyClass<string, string>,您将拥有相同函数的 2 个实例。对于您的基类,这两个实例将位于MyClassBaseandMyClass中,因此其中一个MyClass将隐藏另一个,而不是与之冲突。我想这是否有效取决于您。

于 2014-03-28T19:52:15.137 回答
0

T1我相信这个问题是由于编译器无法揭示如果其中一个或T2类型是另一个(或相同类型)的后代,则应该调用哪个已实现的方法。
想象一下,如果你实例化这个类,它应该做什么MyClass<int, int>

于 2014-03-28T19:55:02.507 回答