简体   繁体   English

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

[英]Implementing covariant interface multiple times: is this behavior properly defined?

Given the following covariant generic interface 给出以下协变通用接口

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

We can create a class that implements this interface multiple times for multiple generic types. 我们可以为多个泛型类型创建一个多次实现此接口的类。 In the scenario I'm interested about, these generic types share a common base type. 在我感兴趣的场景中,这些泛型类型共享一个共同的基类型。

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

Now things get interesting when using this class through a reference of type IContainer<IPrint> . 现在通过IContainer<IPrint>类型的引用使用此类时,事情变得有趣。

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

This compiles and runs without issue and prints "A". 这编译并运行没有问题,并打印“A”。 What I've found in the spec : 我在规范中发现了什么:

The implementation of a particular interface member IM, 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: 特定接口成员IM的实现,其中I是声明成员M的接口,通过检查每个类或结构S来确定,从C开始并重复每个连续的C类基类,直到找到匹配为止:

  • If S contains a declaration of an explicit interface member implementation that matches I and M, then this member is the implementation of IM 如果S包含与I和M匹配的显式接口成员实现的声明,则此成员是IM的实现
  • Otherwise, if S contains a declaration of a non-static public member that matches M, then this member is the implementation of IM 否则,如果S包含与M匹配的非静态公共成员的声明,则此成员是IM的实现

The first bullet point appears to be relevant, because the interface implementations are explicit. 第一个项目符号似乎是相关的,因为接口实现是显式的。 However, it doesn't say anything about which implementation is selected when there are multiple candidates. 但是,当有多个候选者时,它没有说明选择了哪个实现。

It gets even more interesting if we use a public poperty for the IContainer<PrintA> implementation: 如果我们使用公共属性来实现IContainer<PrintA>它会变得更有趣:

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

Now, according to above spec, because there is an explicit interface implementation through IContainer<PrintB> , I would expect this to print "B". 现在,根据上面的规范,因为通过IContainer<PrintB>有一个显式的接口实现,我希望这会打印“B”。 However, it is instead using the public property and still printing "A". 但是,它使用公共属性并仍然打印“A”。

Similarly, if instead I implement IContainer<PrintA> explicitely and IContainer<PrintB> through public property, it still prints "A". 同样,如果我通过公共属性明确地实现IContainer<PrintA> IContainer<PrintB>IContainer<PrintB> ,它仍会打印“A”。

It appears that the only thing the output depends on is the order in which the interfaces are declared. 看来输出唯一依赖的是声明接口的顺序。 If I change the declaration to 如果我将声明更改为

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

everything prints "B"! 一切都打印“B”!

Which part of the spec defines this behavior, if it is properly defined at all? 规范的哪一部分定义了这种行为,如果它被正确定义了?

I'm unable to find it in the specification, but what you're seeing is expected. 我无法在规范中找到它,但你所看到的是预期的。 IContainer<PrintA> and IContainer<PrintB> have different Fully Qualified Names (unable to find spec on how this FQN is formed) and so the compiler recognizes SuperContainer as an implementing class of two different interfaces, each having a void Print(); IContainer<PrintA> IContainer<PrintB> IContainer<PrintA>IContainer<PrintB>具有不同的完全限定名称(无法找到关于如何形成此FQN的规范),因此编译器将SuperContainer识别为两个不同接口的实现类,每个接口都有一个void Print(); method. 方法。

So, we have two different interfaces each containing a method with the same signature. 因此,我们有两个不同的接口,每个接口包含一个具有相同签名的方法。 As you linked in the spec (13.4.2) the implementation of Print() is chosen first by looking at IContainer<PrintA> , looking for a proper mapping, and then looking at IContainer<PrintB> . 当您在规范(13.4.2)中链接时,首先通过查看IContainer<PrintA> ,查找正确的映射, 然后查看IContainer<PrintB>来选择Print() 的实现。

Since a proper mapping was found in IContainer<PrintA> , IContainer<PrintA>.Print() is used in SuperContainer 's implimentation of IContainer<PrintB> . 由于在IContainer<PrintA>找到了正确的映射,因此IContainer<PrintA>.Print() SuperContainer IContainer<PrintA>.Print()用于SuperContainerIContainer<PrintB>

From that same spec (located at the very bottom): 从相同的规范(位于最底部):

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. Class1中的方法F用于Class2的Interface1实现。

So in the end, yes the order is determining which Print() method is called. 所以最后,是的顺序是确定调用哪个Print()方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM