Implementing a covariant interface several times: is this behavior correct?

Given the following covariant common interface

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

We can create a class that implements this interface several times for several types. In the scenario we are interested in, these common types have 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 everything becomes more interesting when using this class using a link like IContainer<IPrint> .

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

It compiles and runs without problems and prints "A". What I found in spec :

The implementation of a specific IM interface, where I am the interface in which the member M is declared, is determined by studying each class or structure S, starting with C and repeating for each subsequent base class C, until a match is found:

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

The first marker point seems relevant because the interface implementations are explicit. However, he does not say anything about which implementation is selected when there are several candidates.

This becomes even more interesting if we use public poperty to implement IContainer<PrintA> :

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

Now, according to the spec above, because there is an explicit interface implementation via IContainer<PrintB> , I expect this to print "B". However, the public property and the seal β€œA” are used instead.

Similarly, if instead I implement IContainer<PrintA> explicitly and IContainer<PrintB> through a public property, it still prints β€œA”.

It appears that only the order in which the interfaces are declared depends on the output. If I change the announcement to

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

everything prints "B"!

What part of the specification defines this behavior, if it is correctly defined at all?

+5
source share
1 answer

I can not find it in the specification, but what you see is expected. IContainer<PrintA> and IContainer<PrintB> have different Fully qualified names (cannot find a specification about how this FQN is formed), and therefore the compiler recognizes SuperContainer as implementing a class of two different interfaces, each of which has a void Print(); method void Print(); .

So, we have two different interfaces, each of which contains a method with the same signature. When you contacted spec (13.4.2) , the Print() implementation is selected first, looking at IContainer<PrintA> , looking for the correct mapping, and then looking at IContainer<PrintB> .

Since the correct mapping was found in IContainer<PrintA> , IContainer<PrintA>.Print() uses the SuperContainer for IContainer<PrintB> .

From the same specification (located at the very bottom):

Members of the base class are involved in the display of the interface. In the example

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

Method F in Class1 is used in the implementation of Class2 Interface1.

So, in the end, yes, the order determines which Print() method is called.

0
source

Source: https://habr.com/ru/post/1240492/


All Articles