多次实现协变接口:这种行为是否正确定义?

时间:2016-01-13 01:19:47

标签: c# covariance

给出以下协变通用接口

public interface IContainer<out T>
{
    T Value { get; }
}

我们可以为多个泛型类型创建一个多次实现此接口的类。在我感兴趣的场景中,这些泛型类型共享一个共同的基类型。

public interface IPrint
{
    void Print();
}
public class PrintA : IPrint
{
    public void Print()
    {
        Console.WriteLine("A");
    }
}
public class PrintB : IPrint
{
    public void Print()
    {
        Console.WriteLine("B");
    }
}

public class SuperContainer : IContainer<PrintA>, IContainer<PrintB>
{
    PrintA IContainer<PrintA>.Value => new PrintA();
    PrintB IContainer<PrintB>.Value => new PrintB();
}

现在通过IContainer<IPrint>类型的引用使用此类时,事情变得有趣。

public static void Main(string[] args)
{
    IContainer<IPrint> container = new SuperContainer();
    container.Value.Print();
}

编译并运行没有问题并打印&#34; A&#34;。我在spec

中找到了什么
  

我所在的特定接口成员I.M的实现   声明成员M的接口由下式确定   检查每个类或结构S,从C开始并重复   C的每个连续基类,直到找到匹配:

     
      
  • 如果是S.   包含显式接口成员实现的声明   匹配I和M,那么这个成员就是I.M。
  • 的实现   
  • 否则,如果S包含非静态公共成员的声明   匹配M,那么这个成员就是I.M。
  • 的实现   

第一个要点似乎是相关的,因为接口实现是显式的。但是,当有多个候选人时,它没有说明选择了哪个实现。

如果我们使用公共属性来实现IContainer<PrintA>,那就更有趣了:

public class SuperContainer : IContainer<PrintA>, IContainer<PrintB>
{
    public PrintA Value => new PrintA();
    PrintB IContainer<PrintB>.Value => new PrintB();
}

现在,根据上面的规范,因为通过IContainer<PrintB>有一个明确的界面实现,我希望这可以打印&#34; B&#34;。但是,它改为使用公共财产并仍然打印&#34; A&#34;。

同样,如果我通过公共财产明确地实施IContainer<PrintA>IContainer<PrintB>,它仍会打印&#34; A&#34;。

看来输出所依赖的唯一依据是声明接口的顺序。如果我将声明更改为

public class SuperContainer : IContainer<PrintB>, IContainer<PrintA>

一切都打印出来&#34; B&#34;!

规范的哪一部分定义了此行为,如果它已正确定义?

1 个答案:

答案 0 :(得分:0)

我无法在规范中找到它,但您所看到的是预期的。 IContainer<PrintA>IContainer<PrintB>具有不同的完全限定名称(无法找到关于如何形成此FQN的规范),因此编译器将SuperContainer识别为两个不同接口的实现类,每个接口都有一个void Print();方法。

因此,我们有两个不同的接口,每个接口都包含一个具有相同签名的方法。当您在spec (13.4.2)中链接时,首先通过查看Print(),查找正确的映射,然后然后查看{{1}来选择IContainer<PrintA>的实现。 }}

由于IContainer<PrintB>中找到了正确的映射,因此IContainer<PrintA> IContainer<PrintA>.Print()的{​​{1}}使用了SuperContainer

从同一规范(位于最底层):

  

基类的成员参与接口映射。在示例中

IContainer<PrintB>
  

Class1中的方法F用于Class2的Interface1实现。

所以最后,是的顺序是确定调用哪个interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2: Class1, Interface1 { new public void G() {} } 方法。