这种继承特质的用例是什么

本文关键字:是什么 继承 | 更新日期: 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 类覆盖 StringContainerItem 属性,继承其string返回类型;但是,它仍然隐藏在 ObjectContainerobject 返回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