【发布时间】: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<IPrint> 类型的引用使用此类时,事情变得有趣了。
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<PrintA> 实现使用公共属性会更有趣:
public class SuperContainer : IContainer<PrintA>, IContainer<PrintB>
{
public PrintA Value => new PrintA();
PrintB IContainer<PrintB>.Value => new PrintB();
}
现在,根据上面的规范,因为通过IContainer<PrintB> 有一个明确的接口实现,我希望这会打印“B”。但是,它改为使用公共属性并仍在打印“A”。
同样,如果我改为通过公共属性显式实现IContainer<PrintA> 和IContainer<PrintB>,它仍然会打印“A”。
看来,输出的唯一依赖是接口声明的顺序。如果我将声明更改为
public class SuperContainer : IContainer<PrintB>, IContainer<PrintA>
一切都打印“B”!
规范的哪一部分定义了这种行为,如果定义正确的话?
【问题讨论】:
-
我认为它是在某处指定的,但我没有看到它。 Eric Lippert 很久以前就有了bleg,所以有人在想这个问题。
标签: c# covariance