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
From
B
将虚拟重载隐藏在
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
以进行覆盖,从而得到预期的结果。因为您有一个public
B
方法,所以它就是被覆盖的方法。感谢您的链接和对页面的提及。尽管如此,我仍然没有看到这种行为的用例,然而从C重写a而不管B的用例是非常明显的。