给出以下协变通用接口
public interface IContainer<out T>
{
T Value { get; }
}
Run Code Online (Sandbox Code Playgroud)
我们可以为多个泛型类型创建一个多次实现此接口的类.在我感兴趣的场景中,这些泛型类型共享一个共同的基类型.
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();
}
Run Code Online (Sandbox Code Playgroud)
现在通过类型引用使用此类时,事情变得有趣IContainer<IPrint>.
public static void Main(string[] args)
{
IContainer<IPrint> container = new SuperContainer();
container.Value.Print();
}
Run Code Online (Sandbox Code Playgroud)
这编译并运行没有问题并打印"A".我在规范中发现了什么:
特定接口成员IM的实现,其中I是声明成员M的接口,通过检查每个类或结构S来确定,从C开始并重复每个连续的C类基类,直到找到匹配为止:
- 如果S包含与I和M匹配的显式接口成员实现的声明,则此成员是IM的实现
- 否则,如果S包含与M匹配的非静态公共成员的声明,则此成员是IM的实现
第一个项目符号似乎是相关的,因为接口实现是显式的.但是,当有多个候选者时,它没有说明选择了哪个实现.
如果我们使用公共属性来IContainer<PrintA>实现它会变得更有趣:
public class SuperContainer : IContainer<PrintA>, IContainer<PrintB>
{
public PrintA Value => new PrintA();
PrintB IContainer<PrintB>.Value => new PrintB();
}
Run Code Online (Sandbox Code Playgroud)
现在,根据上面的规范,因为有一个明确的接口实现IContainer<PrintB>,我希望这打印"B".但是,它使用公共属性并仍然打印"A".
同样,如果我IContainer<PrintA>明确地实现并IContainer<PrintB>通过公共属性,它仍然打印"A".
看来输出唯一依赖的是声明接口的顺序.如果我将声明更改为
public class SuperContainer : IContainer<PrintB>, IContainer<PrintA>
Run Code Online (Sandbox Code Playgroud)
一切都打印"B"!
规范的哪一部分定义了这种行为,如果它被正确定义了?
小智 0
我无法在规范中找到它,但您所看到的是预期的。IContainer<PrintA>并且IContainer<PrintB>具有不同的完全限定名称(无法找到有关如何形成此 FQN 的规范),因此编译器将其识别SuperContainer为两个不同接口的实现类,每个接口都有一个void Print();方法。
因此,我们有两个不同的接口,每个接口都包含具有相同签名的方法。正如您在规范 (13.4.2)Print()中链接的那样,首先通过查看IContainer<PrintA>、寻找正确的映射,然后查看来选择 的实现IContainer<PrintB>。
由于在 中找到了正确的映射IContainer<PrintA>,因此在的实现IContainer<PrintA>.Print()中使用了。SuperContainerIContainer<PrintB>
根据相同的规格(位于最底部):
基类的成员参与接口映射。在示例中
interface Interface1
{
void F();
}
class Class1
{
public void F() {}
public void G() {}
}
class Class2: Class1, Interface1
{
new public void G() {}
}
Run Code Online (Sandbox Code Playgroud)
Class1 中的方法 F 用于 Class2 的 Interface1 实现中。
所以最后,是的,顺序决定了Print()调用哪个方法。