web-dev-qa-db-ja.com

オーバーライドと非表示の正確な違い

誰もがメモリと参照の観点からオーバーライドと非表示の動作を伝えることができますか?.

class A
{
    public virtual void Test1() { //Impl 1}
    public virtual void Test2() { //Impl 2}
}
class B  : A
{
    public override void Test1() { //Impl 3}
    public new void Test2() { Impl 4}
}

static Main()
{
    A aa=new B() //This will give memory to B
    aa.Test1(); //What happens in terms of memory when this executes
    aa.Test2(); //-----------------------SAME------------------------
}

ここで、メモリはクラスBにありますが、2番目のステートメントではaa.Test2クラスAのメソッドが呼び出されます。それはなぜです? Bにメモリがある場合は、Bのメソッドを呼び出す必要があります(私の観点では)。

この基本を非常に深く完全に説明するリンク/演習は、大きな助けになります。

16
Akki J

別の質問に対するこの回答 を見てください。EricLippertによる。

言い換えると(私の理解の限界まで)、これらのメソッドは「スロット」に入ります。 Aには2つのスロットがあります。1つはTest1用、もう1つはTest2用です。

A.Test1virtualとしてマークされ、B.Test1overrideとしてマークされるため、BTest1の実装は独自のスロットを作成せず、Aの実装を上書きします。 BのインスタンスをBとして扱う場合でも、Aにキャストする場合でも、同じ実装がそのスロットにあるため、常にB.Test1の結果が得られます。

対照的に、B.Test2newとマークされているため、独自のnewスロットを作成します。 (newとマークされていないが、別の名前が付けられている場合と同じように。)ATest2の実装は、それ自体のスロットにまだ「あります」。上書きされるのではなく、非表示になっています。 BのインスタンスをBとして扱うと、B.Test2が得られます。それをAにキャストすると、新しいスロットを見ることができずA.Test2が呼び出されます。

28
Rawling

@ Rawlingの回答 に追加するには、次のような例を使用して実際の例を示すことができます。

class Base
{
    // base property
    public virtual string Name
    {
        get { return "Base"; }
    }
}

class Overriden : Base
{
    // overriden property
    public override string Name
    {
        get { return "Overriden"; }
    }
}

class New : Base
{
    // new property, hides the base property
    public new string Name
    {
        get { return "New"; }
    }
}

1。オーバーライド

overridenプロパティの場合、基本クラスの仮想メソッドのスロットは置換されます別の実装による。コンパイラはメソッドをvirtualと見なし、オブジェクトの仮想テーブルを使用して実行時にその実装を解決する必要があります。

{
    Base b = new Base();
    Console.WriteLine(b.Name); // prints "Base"

    b = new Overriden();
    // Base.Name is virtual, so the vtable determines its implementation
    Console.WriteLine(b.Name); // prints "Overriden"

    Overriden o = new Overriden();
    // Overriden.Name is virtual, so the vtable determines its implementation
    Console.WriteLine(o.Name); // prints "Overriden"
}

2。非表示

メソッドまたはプロパティがnewキーワードを使用してhiddenの場合、コンパイラは新しいを作成します派生クラスのみの非仮想メソッド。基本クラスのメソッドは変更されません。

変数のタイプがBaseの場合(つまり、仮想メソッドのみが含まれている場合)、その実装はvtableを介して解決されます。変数のタイプがNewの場合、非仮想メソッドまたはプロパティが呼び出されます。

{
    Base b = new Base();
    Console.WriteLine(b.Name); // prints "Base"

    b = new New();
    // type of `b` variable is `Base`, and `Base.Name` is virtual,
    // so compiler resolves its implementation through the virtual table
    Console.WriteLine(b.Name); // prints "Base"

    New n = new New();
    // type of `n` variable is `New`, and `New.Name` is not virtual,
    // so compiler sees `n.Name` as a completely different property
    Console.WriteLine(n.Name); // prints "New"
}

3。まとめ

コードの一部が基本型を受け入れる場合、実行時に常に仮想テーブルを使用します。ほとんどのOOPシナリオでは、これは、メソッドをnewとしてマークすることは、完全に異なる名前を付けることと非常に似ていることを意味します。

4。インスタンス化後のオブジェクトサイズ

instantiatingこれらのタイプのいずれも、仮想テーブルのコピーを作成しないことに注意してください。各.NETオブジェクトには、数バイトのヘッダーと、そのタイプのテーブルの仮想テーブル(class)へのポインターがあります。

newプロパティ(仮想ではないプロパティ)に関しては、基本的にthiscallセマンティクスを使用して静的メソッドとしてコンパイルされます。つまり、メモリ内のインスタンスのサイズに何も追加されません。

5
Groo

すでに ここ で回答済み

オーバーライドは、同じメソッドシグネチャの複数の可能な実装の定義であり、実装は0番目の引数の実行時型(通常、C#ではthisという名前で識別されます)によって決定されます。

Hidingは、派生型のメソッドの定義であり、その基本型の1つと同じシグネチャをオーバーライドせずに使用します。

オーバーライドと非表示の実際的な違いは次のとおりです。

非表示は他のすべてのメンバー(静的メソッド、インスタンスメンバー、静的メンバー)用です。これは、初期のバインディングに基づいています。より明確に言えば、呼び出されるか使用されるメソッドまたはメンバーは、コンパイル時に決定されます。

•メソッドがオーバーライドされた場合、呼び出す実装は、引数thisの実行時タイプに基づいています。 •メソッドが単に非表示になっている場合、呼び出す実装は、引数thisのコンパイル時の型に基づいています。

ここにいくつかのサンプルがあります: 例#1 。および 例#2

3
Ebad Masood

クラスAのTest1()メソッドとクラスBのtest1()メソッドは、MethdOverridingに従って実行されます。

クラスAのTest2()メソッドとクラスBのtest2()メソッドは、メソッド非表示に従って実行されます。

メソッドではオーバーライド子クラスのメンバーが実行され、メソッドの非表示では親クラスのメンバーが実行されます。

1
Krish

提供されたコードから差し引くと、B:A

基本クラスの(たとえば)メソッドの独自の実装を作成する場合に備えて、メソッドを非表示にすることができます。これはできないオーバーライドされます。たとえば、virtualではありません。 。

私の経験では、私はhidingを主にdebugの目的で使用しました。

たとえば、3番目のprt componentのプロパティを誰が設定するのかわからない場合、誰のコードを使用できません。だから私がすることは:

  • コンポーネントから子クラスを作成します
  • newキーワードで関心のあるプロパティを非表示にします
  • ブレークポイントをsetに置きます
  • ヒットするまで待ちます。

時々、非常に便利で、特に新しいcomponentsframeworkslibraries ..を学習している最初の段階で、情報をすばやく取得するのに役立ちます。

0
Tigran

簡単に言えば、メソッドまたはプロパティをオーバーライドする場合、オーバーライドメソッドは基本メソッドと同じシグネチャを持っている必要があります。これを非表示にする必要がない場合、新しいオブジェクトは次のように任意の形式をとることができます

// base
    public int GrossAmount { get; set; }

    // hiding base
    public new string GrossAmount
    {
        get;
        set;             
    }
0
Johnv2020
class Base {
    int a;
    public void Addition() {
        Console.WriteLine("Addition Base");
    }
    public virtual void Multiply()
    {
        Console.WriteLine("Multiply Base");
    }
    public void Divide() {
        Console.WriteLine("Divide Base");
    }
}

class Child : Base
{
    new public void Addition()
    {
        Console.WriteLine("Addition Child");
    }
    public override void Multiply()
    {
        Console.WriteLine("Multiply Child");
    }
    new public void Divide()
    {
        Console.WriteLine("Divide Child");
    }
}
class Program
{        
    static void Main(string[] args)
    {
        Child c = new Child();
        c.Addition();
        c.Multiply();
        c.Divide();

        Base b = new Child();
        b.Addition();
        b.Multiply();
        b.Divide();

        b = new Base();
        b.Addition();
        b.Multiply();
        b.Divide();
    }
}

出力:-

追加の子

子を掛ける

子を分割する

追加ベース

子を掛ける

ベースを分割する

追加ベース

基数を掛ける

ベースを分割する

コンパイラは、オーバーライド時にクラスのオブジェクトをチェックしますが、非表示の場合、コンパイラはクラスの参照のみをチェックします。

0
Shubham Sharma
 public class BaseClass
    {
      public void PrintMethod()
      {
       Console.WriteLine("Calling base class method");
      }
     }
     public class ChildClass
     {
      public new void PrintMethod()
      {
       Console.WriteLine("Calling the child or derived class method");
       }
      }
      class Program
      {
       static void Main()
       {
        BaseClass bc = new ChildClass();
        bc.PrintMethod();
        }
       }

メソッドの非表示は、基本クラスの参照変数が子クラスオブジェクトを指している場合です。基本クラスの隠しメソッドを呼び出します。

一方、基本クラスで仮想メソッドを宣言する場合。派生クラスまたは子クラスでそのメソッドをオーバーライドします。次に、基本クラス参照変数は派生クラスメソッドを呼び出します。これはメソッドのオーバーライドと呼ばれます。

0
Gagan

メソッドまたはプロパティを非表示にすることで、そのタイプのオブジェクトがある場合に、そのようなメソッドが多形になるのを止めたいと言っているだけです。さらに、非表示のメソッドは非多態的な方法で呼び出されるため、これらのメソッドタイプを呼び出すには、単純に非仮想メソッドであるため、コンパイル時に知る必要があります。

0
Felice Pollano