以下は単純化しすぎており、可能な実装の 1 つを事実として扱っていますが、メンタル モデルを機能させるには十分なはずです。
呼び出しコードがクラスを「知っている」場合、次のことを知っています。
フィールドは、オブジェクトの位置から特定のオフセットでアクセスできます。たとえば、オブジェクトがアドレス 120 にあり、2 つの整数フィールドがある場合、アドレス 124 にあるそれらの 1 つにアクセスできる可能性があります。同じタイプの別のオブジェクトがアドレス 140 にある場合、同等のフィールドは 144 になります。
非仮想メソッド (およびプロパティは、1 つまたは 2 つのメソッドのシンタックス シュガーと見なすことができます) は、(this
メソッド内から) 呼び出しているオブジェクトへの参照とその関数の他のパラメーターを受け取る特定のアドレスにある関数です。
仮想メソッドは上記のようなものですが、それらのアドレスは、クラスに関連付けられたテーブル内の特定のオフセットを調べることで見つけることができます。そのアドレスは、クラスのアドレスからの特定のオフセットでもあります。
これにKid
は、のスーパーセットであるメソッドのテーブルがありParent
(さらにメソッドを追加できます)、オーバーライドしなかったメソッドに対して同じ関数アドレスを持ちます (Equals
それを呼び出すと、呼び出すのと同じ関数が使用されます) )、ただしEquals
、Parent
オーバーライドするものとは別のアドレス(Print()
この場合)。
したがって、参照または参照Kid
を介して取得した場合、呼び出しは同じテーブルを参照し、メソッドの場所を検索して呼び出します。Parent
Kid
Print()
Print()
の場合、メソッド上Child
でnew
使用されPrint
ます。これは、別のテーブルが必要であることをコンパイラに伝えます。したがって、参照Print()
を介して呼び出すと、固有のテーブルが検索され、見つかったメソッドが呼び出されます。ただし、または参照を介して呼び出すと、使用できる特定のテーブルがあることさえわからず、知っているテーブルと持っているテーブルで関数を検索し、見つかった関数を呼び出します (そので定義)。Child
Child
Kid
Parent
Child
Kid
Parent
Kid
原則として、new
避けるべきです。用途は以下の2箇所です。
1 つは下位互換性です。たとえばChild
、プロパティを持っていて、Name
後でコードParent
が変更されてプロパティも持つようになった場合Name
、競合が発生します。はオーバーライドではないため、オーバーライドがあるかChild
のName
ように扱われますがnew
、警告が表示されます。これは、古い方法を使用するコードと新しいName
onを認識しているコードParent
が共存できる唯一の方法だからです。再コンパイルに戻る場合Child
は、独自のものを持たないようにリファクタリングするかName
(それが必要な場合Parent
)、オーバーライドになるようにリファクタリングするか、まったく別のものにリファクタリングするか、それnew
を示すために追加する必要があります。これは、理想的とは言えませんが、私たちが望んでいる方法です。
もう 1 つはnew
、基本クラスのメソッドで許可されているのと同じ動作のより具体的な形式を許可する場合ですが、論理的に互換性があります (ユーザーが驚くことはありません)。この後者は、やや高度なテクニックのボックスに入れる必要があり、簡単に行うべきではありません。new
ほとんどの場合、見るということは、せいぜい妥協であり、おそらく改善する必要がある何かに対処することを意味するため、そのようにコメントする必要もあります.
(余談ですが、レンがいるKid
のを見てタブロイド紙を思い浮かべたのは私だけですか?)Child