18

継承されたクラスを継承する場合、new/overrideの動作は私が期待するものではありません。

$ 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 CnewここでBクラスの修飾子が優先されるのはなぜですか?そのユースケースは何ですか?特に、Cが実際にAをオーバーライドするという明らかなユースケースを破るからです。

上記のコードは、Debianから派生したディストリビューションで実行されているMono2.10で実行されていることに注意してください。しかし、MS Visual StudioのC#コンパイラを使用して同じ動作を確認しました。

4

5 に答える 5

21

new修飾子によりメンバーが非表示になり、クラス階層のポリモーフィックな関係が失われます。のSayHiメソッドは、'sとは異なる(オーバーライドではない)Bものとして扱われます(したがって、キーワードとして「new」という単語を選択します)。次に、'sメソッドは'sではなく'sをオーバーライドします(これは非表示のままです)。ACBA

したがって、参照を介しSayHiCインスタンスを呼び出すAと、ランタイムはAタイプではなくCタイプ(SayHiから継承された「新しい」メソッドB)に対してインスタンスを解決します。

一方、実行する場合:

B p = new C();
p.SayHi();

…期待される多形の結果が得られます。

From C

編集:ユースケースをリクエストしたので、これが1つです。.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 + "\""; }
    }
}

クラスのItemプロパティはObjectContainerプレーンを返します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オーバーライドし、その戻り型を継承します。ただし、それはまだの-returningプロパティから隠されています。この方法でなければ、異種の返品タイプを調整する方法はありません…</ p> ItemStringContainerstringobjectItemObjectContainer

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
于 2012-06-14T18:48:28.757 に答える
7

Cメソッドのシャドウバージョン(でシャドウされている)をオーバーライドし、のメソッドBをオーバーライドしませんA

その結果、タイプの変数を使用している場合、でオーバーライドされないためA、でSayHi定義されたAが呼び出されます。C

于 2012-06-14T18:48:33.440 に答える
6

CのメソッドをオーバーライドBするため、にキャストするとA、で定義された仮想を呼び出すことになりますA

ECMA 334: 17.5.3のC#言語仕様がほとんどあなたの例です(294ページ)を参照してください。

于 2012-06-14T18:50:25.293 に答える
2

CクラスはAクラスのSayHiメソッドをオーバーライドしないため、Bの「new」メソッドをオーバーライドします。キャストはAに対するものであるため、コンパイラーはそれをCではなくA.SayHi()の呼び出しとして解決します。 SayHi()

于 2012-06-14T18:50:59.277 に答える
2

このmsdnページの最後の例では、ここで何が起こっているかを詳しく説明しています。

基本的に、新しい修飾子により、AのメソッドはCから非表示になり、Cがオーバーライドするとパブリックになるため、Bのメソッド(独自のメソッドとして扱われます)がオーバーライドされます。

Bのメソッドをprivateに設定すると、Cは再びAのメソッドをオーバーライドします。

class B : A
{
    private new void SayHi()
    {
        Console.WriteLine("From B");
    }
}

結果:From C

于 2012-06-14T18:52:47.007 に答える