为什么这个C#代码会返回它的功能

B-L*_*k-a 37 c# interface code-snippets

有人可以帮我理解为什么这段代码片段返回"Bar-Bar-Quux"?在阅读界面后,我很难理解这一点.

interface IFoo
{ 
    string GetName();
}

class Bar : IFoo
{
    public string GetName() { return "Bar"; }
}

class Baz : Bar
{
    public new string GetName() { return "Baz"; }
}

class Quux : Bar, IFoo
{
    public new string GetName() { return "Quux"; }
}

class Program
{
    static void Main()
    {
        Bar f1 = new Baz();
        IFoo f2 = new Baz();
        IFoo f3 = new Quux();
        Console.WriteLine(f1.GetName() + "-" + f2.GetName() + "-" + f3.GetName());
    }
}
Run Code Online (Sandbox Code Playgroud)

Mik*_*ray 57

这里发生了两件事.一个是成员隐藏.这是众所周知的,并在其他地方有所涉及.另一个鲜为人知的特性是C#5规范第13.4.6节中介绍的接口重新实现.报价:

允许继承接口实现的类通过将其包含在基类列表中来重新实现接口.接口的重新实现遵循与接口的初始实现完全相同的接口映射规则.因此,继承的接口映射对于为接口的重新实现而建立的接口映射没有任何影响.

继承的公共成员声明和继承的显式接口成员声明参与重新实现的接口的接口映射过程.

结果f1.GetName()是"Bar",因为该方法Baz.GetName隐藏Bar.GetNamef1声明为类型Bar.除非将其显式声明为虚拟和重写,否则不会对运行时类型的实现进行调度.

类似地,for f2.GetName(),Baz.GetName隐藏了实现Bar,因此在通过对接口的引用使用dispatch时不会调用它.接口被"映射"到声明的方法,Bar因为这是声明接口的类型.Baz具有相同名称的兼容方法无关紧要.接口映射的规则在规范的第13.4.4节中定义.如果GetName已声明为虚拟Bar,则可以覆盖它,然后通过接口调用.结果因此也是"Bar".

对于f3.GetName(),Quux重新实现,IFoo以便定义自己的映射GetName.请注意,它还隐藏了从中继承的实现Bar.没有必要使用new来重新实现,它只是抑制了有关隐藏的警告.因此结果是"Quux".

这就解释了你看到的输出:"Bar-Bar-Quux"

Eric Lippert的这篇文章讨论了这个棘手功能的一些细微差别.


Aus*_*ins 8

根据定义,接口没有相关的实现,也就是说它们的方法总是虚拟的和抽象的.相反,Bar上面的类定义了一个具体的实现GetName.这满足了实施所需的合同IFoo.

Baz现在继承Bar并声明一个new方法GetName.也就是说,父类Bar有一个具有相同名称的方法,但在Baz明确使用对象时会完全忽略它.

然而,如果一个Baz对象转换为一个Bar,或者干脆分配给类型的变量Bar或者IFoo,因为它告诉举止象它会做Bar.换句话说,方法名称GetName是指Bar.GetName而不是Baz.GetName.

现在,在第三种情况下,Quux都继承自Bar 实现IFoo.现在,当演员表演时IFoo,它将提供自己的实现(根据Mike Z的答案中提供的规范).

然而,当Quux被转换为Bar时,它返回"Bar",就像Baz一样.