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

C# 为什么这个C代码会返回它的功能,c#,interface,code-snippets,C#,Interface,Code Snippets,有人能帮我理解为什么这个代码段返回“Bar-qux”吗?即使在阅读了接口之后,我也很难理解这一点 interface IFoo { string GetName(); } class Bar : IFoo { public string GetName() { return "Bar"; } } class Baz : Bar { public new string GetName() { return "Baz"; } } class Quux : Bar, I

有人能帮我理解为什么这个代码段返回“Bar-qux”吗?即使在阅读了接口之后,我也很难理解这一点

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

这里发生了两件事。一个是成员隐藏。这是一个相当知名和涵盖。另一个鲜为人知的特性是C#5规范第13.4.6节中介绍的接口重新实现。引述:

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

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

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());
    }
}
f1.GetName()
的结果是“Bar”,因为方法
Baz.GetName
隐藏了
Bar。GetName
并且
f1
声明为类型
Bar
。除非运行时类型的实现显式声明为虚拟并被重写,否则不会对其进行调度

类似地,对于
f2.GetName()
Baz.GetName
将实现隐藏在
Bar
中,因此在通过引用接口使用dispatch时不会调用它。接口被“映射”到在
Bar
中声明的方法,因为这是声明接口的类型。
Baz
是否有同名的兼容方法并不重要。规范第13.4.4节定义了接口映射规则。如果
GetName
已在
Bar
中声明为虚拟,则可以覆盖该规则,然后通过接口调用该规则。因此,结果也是“酒吧”

对于
f3.GetName()
qux
重新实现
IFoo
,因此它可以定义自己到
GetName
的映射。请注意,它还隐藏了从
Bar
继承的实现。不必使用new来执行重新实现,它只是抑制关于隐藏的警告。因此,结果是“Quux”

这就解释了你看到的输出:“Bar-qux”


Eric Lippert的这篇文章讨论了这个棘手的特性中的一些细微差别。

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

Baz
现在继承自
Bar
并声明一个
new
方法
GetName
。也就是说,父类
Bar
有一个同名的方法,但在显式处理
Baz
对象时,它被完全忽略

但是,如果将
Baz
对象强制转换为
Bar
,或者简单地将其分配给
Bar
IFoo
类型的变量,则它将按照指示操作,并表现为
Bar
。换句话说,方法名
GetName
指的是
Bar.GetName
,而不是
Baz.GetName

现在,在第三种情况下,
qux
都继承自
Bar
,并实现
IFoo
。现在,当转换为
IFoo
时,它将提供自己的实现(根据Mike Z的回答中提供的规范)


但是,当Quux转换为Bar时,它会返回“Bar”,就像Baz一样。

在控制台中对GetName()进行3次调用后,输出为Bar Quux。WriteLine方法调用

Bar f1 = new Baz();
IFoo f2 = new Baz();
IFoo f3 = new Quux();
Console.WriteLine(f1.GetName() + "-" + f2.GetName() + "-" + f3.GetName());
//Bar-Bar-Quux
让我们检查每个调用,以便更清楚地了解发生了什么

f1.GetName()

f1
被实例化为
Baz
。但是,它是键入的作为
。因为
Bar
公开了
GetName
,当使用
f1.GetName()
时,这就是被调用的方法-不管
Baz
还实现了
GetName
。原因是
f1
没有键入为
Baz
,如果是,它将调用
Baz
GetName
方法。这方面的一个例子是检查

Console.WriteLine(((Baz)f1).GetName() + "-" + f2.GetName() + "-" + f3.GetName());
//Baz-Bar-Quux
这是可能的,因为有两个事实。首先,
f1
最初被实例化为
Baz
,它被简单地键入为
Bar
。其次,
Baz
确实有一个
GetName
方法,在其定义中使用
new
隐藏了继承的
Bar
GetName
方法,允许调用
Baz
GetName

f2.GetName()

一个非常相似的类型出现在f2上,它被定义为

IFoo f2 = new Baz();
虽然Baz类实现了
GetName
方法,但它没有实现
IFoo
GetName
方法,因为
Baz
没有从
IFoo
继承,因此该方法不可用
Bar
实现
IFoo
,由于
Baz
继承自
Bar
Bar
GetName
是当
f2
键入为
IFoo
时公开的方法

同样,由于
f2
最初被实例化为
Baz
,因此它仍然可以转换为
Baz

Console.WriteLine(f1.GetName() + "-" + ((Baz)f2).GetName() + "-" + f3.GetName());
//Bar-Baz-Quux
由于上述原因,
f1
f2
最初输入为
Baz
,而
Baz
GetName
方法隐藏继承的
GetName
方法)