C# 这种继承特性的用例是什么?
继承继承的类时,新的/重写行为不是我所期望的:C# 这种继承特性的用例是什么?,c#,inheritance,mono,C#,Inheritance,Mono,继承继承的类时,新的/重写行为不是我所期望的: $ 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
$ 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()方法,我希望输出是C的。为什么B类的new
修饰符在这里优先?这方面的用例是什么?特别是因为它打破了C确实凌驾于A之上的明显用例
请注意,上面的代码是在Debian衍生发行版上运行的Mono 2.10上运行的。但我已经在MS Visual Studio中使用C#编译器确认了相同的行为。导致成员隐藏,从而破坏了类层次结构中的多态关系。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
类的项
属性返回一个普通的对象
。但是,在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
的对象
-返回项
属性隐藏。如果不是这样,就没有办法协调他们不同的回报类型
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
中定义的虚拟变量
请参见17.5.3中的示例(第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
new
FromB
将虚拟重载隐藏在C
中。将p
更改为键入B
,您将看到C
的输出问题是“这个用例是什么?”但实际问题是“它为什么这样做?”。我很好奇这个用例是什么——故意以一种对API的消费者来说不明显的方式破坏继承似乎非常危险。谢谢。我理解更改B将如何运行C的方法,但是否存在打破a和C之间多态关系的用例?编程C的家伙可能有很好的理由重写A的方法,但我看不到防止这种情况的用例,不管B是定义为new
还是override
。因为C
派生自B
,后者派生自A
,C
和A
之间没有直接的多态关系,只有通过B
间接的多态关系。如果B
–A
链断开,那么C
对A
没有进一步的要求,因为B
断开了通道,我认为这是有道理的。谢谢道格拉斯。@dotancohen:我添加了一个use类;检查您是否同意。谢谢,但该示例引用了私有方法。@dotancohen是的,因为在B
private中创建该方法会将底层A
方法暴露给C
以进行覆盖,从而得到预期的结果。因为您有一个publicB
方法,所以它就是被覆盖的方法。感谢您的链接和对页面的提及。尽管如此,我仍然没有看到这种行为的用例,然而从C重写a而不管B的用例是非常明显的。