【问题标题】:Implementing covariant interface multiple times: is this behavior properly defined?多次实现协变接口:这种行为是否正确定义?
【发布时间】:2016-01-13 01:19:46
【问题描述】:

给定以下协变泛型接口

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&lt;IPrint&gt; 类型的引用使用此类时,事情变得有趣了。

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

这编译和运行没有问题并打印“A”。我在spec 中发现了什么:

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

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

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

如果我们为 IContainer&lt;PrintA&gt; 实现使用公共属性会更有趣:

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

现在,根据上面的规范,因为通过IContainer&lt;PrintB&gt; 有一个明确的接口实现,我希望这会打印“B”。但是,它改为使用公共属性并仍在打印“A”。

同样,如果我改为通过公共属性显式实现IContainer&lt;PrintA&gt;IContainer&lt;PrintB&gt;,它仍然会打印“A”。

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

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

一切都打印“B”!

规范的哪一部分定义了这种行为,如果定义正确的话?

【问题讨论】:

  • 我认为它是在某处指定的,但我没有看到它。 Eric Lippert 很久以前就有了bleg,所以有人在想这个问题。

标签: c# covariance


【解决方案1】:

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

所以,我们有两个不同的接口,每个接口都包含一个具有相同签名的方法。正如您在spec (13.4.2) 中链接的那样,首先通过查看IContainer&lt;PrintA&gt; 选择Print() 的实现,寻找适当的映射,然后然后查看IContainer&lt;PrintB&gt;

由于在IContainer&lt;PrintA&gt; 中找到了正确的映射,因此SuperContainerIContainer&lt;PrintB&gt; 的实现中使用了IContainer&lt;PrintA&gt;.Print()

来自同一规范(位于最底部):

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

interface Interface1
{
   void F();
}
class Class1
{
   public void F() {}
   public void G() {}
}
class Class2: Class1, Interface1
{
   new public void G() {}
}

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

所以最后,是的,顺序决定了调用哪个Print() 方法。

【讨论】:

  • 您可以从规范中的哪个文本中准确得出“Print() 的实现首先通过查看IContainer&lt;PrintA&gt; 来选择,寻找适当的映射,然后然后 i> 看着IContainer&lt;PrintB&gt;。”?
猜你喜欢
  • 1970-01-01
  • 2014-08-07
  • 2018-11-13
  • 1970-01-01
  • 1970-01-01
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多