这种继承特质的用例是什么
本文关键字:是什么 继承 | 更新日期: 2023-09-27 18:37:18
继承继承类时,新的/覆盖行为不是我所期望的:
$ cat Program.cs
using System;
class A {
public virtual void SayHi() {
Console.WriteLine("From A");
}
}
class B : A {
public new virtual void SayHi() {
Console.WriteLine("From B");
}
}
class C : B {
public override void SayHi() {
Console.WriteLine("From C");
}
}
public class Program {
public static void Main() {
A p = new C();
p.SayHi();
}
}
$ ./Program.exe
From A
由于类 C 覆盖了 sayHi() 方法,我希望输出From C
。为什么 B 类的 new
修饰符在这里优先?这样做的用例是什么?特别是当它打破了让 C 真正覆盖 A 的明显用例时。
请注意,上面的代码是在 Debian 派生发行版上运行的 Mono 2.10 上运行的。但是我已经在MS Visual Studio中使用C#编译器确认了相同的行为。
new
修饰符会导致成员隐藏,从而破坏类层次结构中的多态关系。B
SayHi
方法被视为与A
不同的(而不是覆盖)(因此选择单词"new"作为关键字)。 然后C
的方法覆盖B
,而不是A
的(保持隐藏)。
因此,当您通过A
引用在C
实例上调用SayHi
时,运行时将针对A
类型而不是C
类型(其中SayHi
是从B
继承的"新"方法)解析它。
另一方面,如果您要运行:
B p = new C();
p.SayHi();
。你会得到预期的多态结果:
From C
编辑:既然你请求了一个用例,这里有一个。在 .NET Framework 2.0 中引入泛型之前,成员隐藏有时用作更改派生类中继承方法的返回类型的一种手段(重写时无法执行此操作),以便返回更具体的类型。例如:
class ObjectContainer
{
private object item;
public object Item
{
get { return item; }
set { item = value; }
}
}
class StringContainer : ObjectContainer
{
public new virtual string Item
{
get { return base.Item as string; }
set { base.Item = value as string; }
}
}
class QuotedStringContainer : StringContainer
{
public override string Item
{
get { return "'"" + base.Item + "'""; }
}
}
ObjectContainer
类的 Item
属性返回一个普通object
。但是,在StringContainer
中,这个继承的属性被隐藏以返回一个string
。因此:
ObjectContainer oc = new StringContainer();
object o = oc.Item; // Valid, since ObjectContainer.Item is resolved
string s1 = oc.Item; // Not valid, since ObjectContainer.Item is still resolved
string s2 = ((StringContainer)oc).Item;
// Valid, since StringContainer.Item is now resolved
QuotedStringContainer
类覆盖 StringContainer
的 Item
属性,继承其string
返回类型;但是,它仍然隐藏在 ObjectContainer
的 object
返回Item
属性中。如果不是这样,就没有办法调和他们不同的返回类型......
ObjectContainer oc = new QuotedStringContainer();
object o = oc.Item; // Valid, since ObjectContainer.Item is resolved
string s1 = oc.Item; // Not valid, since ObjectContainer.Item is still resolved
string s2 = ((StringContainer)oc).Item;
// Valid, since QuotedStringContainer.Item is now resolved
// (polymorphism!)
string s3 = ((QuotedStringContainer)oc).Item;
// Valid, since QuotedStringContainer.Item is now resolved
C
覆盖方法的阴影版本(在B
中被阴影化),而不是覆盖A
中的阴影版本。
因此,当您使用类型 A
的变量时,将调用 A
中定义的SayHi
,因为它不会在 C
中被覆盖。
C
会覆盖B
的方法,所以当你把它投射到A
时,你最终会调用A
中定义的虚拟。
请参阅 ECMA 334:17.5.3 下的 C# 语言规范几乎就是您的示例(第 294 页)。
因为 C 类没有重写 A 类中的 SayHi 方法,所以它重写了 B 类中的"new"方法。由于您的强制转换为 A,因此编译器将其解析为 对 A.SayHi() 的调用,而不是 C.SayHi()
此 msdn 页面中的最后一个示例密切解释了此处发生的情况。
基本上,新的修饰符导致 A 中的方法对 C 隐藏,并且由于它在 C 覆盖时是公共的,因此它覆盖了 B 中的方法(被视为其自己的独特方法)。
如果将 B 中的方法设置为 private,C 将再次重写 A 中的方法。
class B : A
{
private new void SayHi()
{
Console.WriteLine("From B");
}
}
结果在: From C