c# - 多次实现协变接口(interface) : is this behavior properly defined?

标签 c# covariance

给定以下协变泛型接口(interface)

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

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

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();
}

这可以毫无问题地编译和运行并打印“A”。我在 spec 中找到了什么:

The implementation of a particular interface member I.M, where I is the interface in which the member M is declared, is determined by examining each class or struct S, starting with C and repeating for each successive base class of C, until a match is located:

  • If S contains a declaration of an explicit interface member implementation that matches I and M, then this member is the implementation of I.M.
  • Otherwise, if S contains a declaration of a non-static public member that matches M, then this member is the implementation of I.M.

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

如果我们为 IContainer<PrintA> 使用公共(public)属性,它会变得更加有趣实现:

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

现在,根据上面的规范,因为通过 IContainer<PrintB> 有一个明确的接口(interface)实现,我希望这会打印“B”。但是,它使用公共(public)属性并仍然打印“A”。

同样,如果我改为执行 IContainer<PrintA>明确地和IContainer<PrintB>通过公共(public)属性(property),它仍然打印“A”。

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

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

一切都打印“B”!

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

最佳答案

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

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

由于在 IContainer<PrintA> 中找到了正确的映射, IContainer<PrintA>.Print()用于 SuperContainerIContainer<PrintB> 的实现.

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

The members of a base class participate in interface mapping. In the example

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

the method F in Class1 is used in Class2's implementation of Interface1.

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

关于c# - 多次实现协变接口(interface) : is this behavior properly defined?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34756842/

相关文章:

c# - 将控件拖放到自定义用户控件上会隐藏

c# - 使用 Debenu PDFViewer 在磁盘上保存附件文件失败

c# - DotNet Core - 加密哈希函数返回不一致的结果

python - Numpy - 两个矩阵的行之间的协方差

c# - 为什么 C# 在带有委托(delegate)的输入参数中使用逆变(而不是协方差)?

c# - 将 SAXON 9.5 (nuget) 与 Schematron 结合使用

c# - 你如何使用 String.IsNullOrEmpty 单元测试代码?

C#:私有(private)内部接口(interface)是否可能?

c# - Rust 中 fn 参数的协方差使用什么?

Swift:协变覆盖?