1
class Program
{
    static void Main(string[] args)
    {
        Parent p = new Child();
        p.Print();
    }
}
class Parent
{
    public virtual void Print()
    {
        Console.WriteLine("This is parent.");
    }
}
class Kid:Parent
{
    public override void Print()
    {
        Console.WriteLine("This is Kid.");
    }
}
class Child : Kid
{
    public new virtual void Print()
    {
        Console.WriteLine("This is Child.");
    }
}  

「This is child」ではなく「This is Kid」という出力が表示されるのはなぜですか?

Print()クラス内はChild仮想です。
私は何が起こっているのか理解しようとしています。

4

5 に答える 5

1

のオーバーライドされたPrintメソッドを呼び出していParentます。

newその変数を。として入力すると、演算子が機能しますKid。この場合、new はオーバーライドではなく、識別子の再利用です。

于 2012-08-14T10:10:35.620 に答える
1

pタイプParentです。したがって、コンパイラはクラスPrint内のメソッドを探します。Parentこのメソッドは仮想であるため、クラス内でオーバーライドされたメソッドを検出しKidます。のメソッドをオーバーライドせずに置き換えたため、コンパイラはこのメソッドを使用しません。PrintChild

于 2012-08-14T10:10:43.720 に答える
0

新しいキーワードを使用したので、継承されたメソッドを非表示にして、新しい実装を提供したことを意味します。これは通常、親メンバーの非表示と呼ばれます。

于 2012-08-14T10:11:04.950 に答える
0

以下は単純化しすぎており、可能な実装の 1 つを事実として扱っていますが、メンタル モデルを機能させるには十分なはずです。

呼び出しコードがクラスを「知っている」場合、次のことを知っています。

  1. フィールドは、オブジェクトの位置から特定のオフセットでアクセスできます。たとえば、オブジェクトがアドレス 120 にあり、2 つの整数フィールドがある場合、アドレス 124 にあるそれらの 1 つにアクセスできる可能性があります。同じタイプの別のオブジェクトがアドレス 140 にある場合、同等のフィールドは 144 になります。

  2. 非仮想メソッド (およびプロパティは、1 つまたは 2 つのメソッドのシンタックス シュガーと見なすことができます) は、(thisメソッド内から) 呼び出しているオブジェクトへの参照とその関数の他のパラメーターを受け取る特定のアドレスにある関数です。

  3. 仮想メソッドは上記のようなものですが、それらのアドレスは、クラスに関連付けられたテーブル内の特定のオフセットを調べることで見つけることができます。そのアドレスは、クラスのアドレスからの特定のオフセットでもあります。

これにKidは、のスーパーセットであるメソッドのテーブルがありParent(さらにメソッドを追加できます)、オーバーライドしなかったメソッドに対して同じ関数アドレスを持ちます (Equalsそれを呼び出すと、呼び出すのと同じ関数が使用されます) )、ただしEqualsParentオーバーライドするものとは別のアドレス(Print()この場合)。

したがって、参照または参照Kidを介して取得した場合、呼び出しは同じテーブルを参照し、メソッドの場所を検索して呼び出します。ParentKidPrint()Print()

の場合、メソッド上Childnew使用されPrintます。これは、別のテーブルが必要であることをコンパイラに伝えます。したがって、参照Print()を介して呼び出すと、固有のテーブルが検索され、見つかったメソッドが呼び出されます。ただし、または参照を介して呼び出すと、使用できる特定のテーブルがあることさえわからず、知っているテーブルと持っているテーブルで関数を検索し、見つかった関数を呼び出します (そので定義)。ChildChildKidParentChildKidParentKid

原則として、new避けるべきです。用途は以下の2箇所です。

1 つは下位互換性です。たとえばChild、プロパティを持っていて、Name後でコードParentが変更されてプロパティも持つようになった場合Name、競合が発生します。はオーバーライドではないため、オーバーライドがあるかChildNameように扱われますがnew、警告が表示されます。これは、古い方法を使用するコードと新しいNameonを認識しているコードParentが共存できる唯一の方法だからです。再コンパイルに戻る場合Childは、独自のものを持たないようにリファクタリングするかName(それが必要な場合Parent)、オーバーライドになるようにリファクタリングするか、まったく別のものにリファクタリングするか、それnewを示すために追加する必要があります。これは、理想的とは言えませんが、私たちが望んでいる方法です。

もう 1 つはnew、基本クラスのメソッドで許可されているのと同じ動作のより具体的な形式を許可する場合ですが、論理的に互換性があります (ユーザーが驚くことはありません)。この後者は、やや高度なテクニックのボックスに入れる必要があり、簡単に行うべきではありません。newほとんどの場合、見るということは、せいぜい妥協であり、おそらく改善する必要がある何かに対処することを意味するため、そのようにコメントする必要もあります.

(余談ですが、レンがいるKidのを見てタブロイド紙を思い浮かべたのは私だけですか?)Child

于 2012-08-14T10:35:13.427 に答える
0

MSDN から ( http://msdn.microsoft.com/en-us/library/6fawty39(v=vs.100).aspx ):

「Derived のインスタンスで DoWork が呼び出されると、C# コンパイラは最初に Derived で最初に宣言された DoWork のバージョンと互換性のある呼び出しを作成しようとします。オーバーライド メソッドはクラスで宣言されているとは見なされず、メソッドの新しい実装です。基本クラスで宣言されています。C# コンパイラがメソッド呼び出しを Derived の元のメソッドに一致させることができない場合にのみ、同じ名前と互換性のあるパラメーターを持つオーバーライドされたメソッドへの呼び出しを一致させようとします。」

Kid で「new」を使用しているように見えますが、p を Parent として宣言すると、継承階層の一部ではないため、p は Print in Child を参照できません。

static void Main(string[] args)
{
    Parent p = new Child();
    p.Print();

    Child c = (Child) p;
    c.Print();
}

...明らかに物事が変わります。

于 2012-08-14T10:18:38.827 に答える