C# 为什么这个C代码会返回它的功能
有人能帮我理解为什么这个代码段返回“Bar-qux”吗?即使在阅读了接口之后,我也很难理解这一点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
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
方法)