web-dev-qa-db-ja.com

仮想、オーバーライド、新規、および封印オーバーライドの違い

OOPのいくつかの概念の間でかなり混乱しています:virtualoverridenewおよびsealed override。誰でも違いを説明できますか?

派生クラスメソッドを使用する場合は、overrideキーワードを使用して、派生クラスで基本クラスメソッドをオーバーライドできることは明らかです。しかし、newsealed overrideについてはわかりません。

76
Romil N

virtualキーワードを使用して、メソッド、プロパティ、インデクサー、またはイベント宣言を変更し、派生クラスでオーバーライドできるようにします。たとえば、このメソッドは、それを継承するクラスでオーバーライドできます。新しい修飾子を使用して、基本クラスから継承されたメンバーを明示的に非表示にします。継承されたメンバーを非表示にするには、同じ名前を使用して派生クラスで宣言し、新しい修飾子で変更します。

これはすべてポリモーフィズムと関係しています。参照に対して仮想メソッドが呼び出されると、参照が参照するオブジェクトの実際のタイプを使用して、使用するメソッド実装を決定します。派生クラスで基本クラスのメソッドがオーバーライドされると、オブジェクトが派生クラスのインスタンスであることを呼び出し元のコードが「知らなかった」場合でも、派生クラスのバージョンが使用されます。例えば:

public class Base
{
  public virtual void SomeMethod()
  {
  }
}

public class Derived : Base
{
  public override void SomeMethod()
  {
  }
}

...

Base d = new Derived();
d.SomeMethod();

base.SomeMethodをオーバーライドする場合、Derived.SomeMethodを呼び出すことになります。

overrideの代わりにnewキーワードを使用すると、派生クラスのメソッドは基本クラスのメソッドをオーバーライドせず、単に非表示にします。その場合、次のようなコード:

public class Base
{
  public virtual void SomeOtherMethod()
  {
  }
}

public class Derived : Base
{
  public new void SomeOtherMethod()
  {
  }
}

...


Base b = new Derived();
Derived d = new Derived();
b.SomeOtherMethod();
d.SomeOtherMethod();

最初にBase.SomeOtherMethodを呼び出し、次にDerived.SomeOtherMethodを呼び出します。これらは、基本メソッドをオーバーライドする派生メソッドではなく、事実上同じ名前を持つ2つの完全に独立したメソッドです。

Newまたはoverrideのいずれも指定しない場合、結果の出力はnewを指定した場合と同じですが、コンパイラーの警告も表示されます(基本クラスのメソッドを非表示にしていることに気付かない場合があるため)メソッド、または実際にそれをオーバーライドしたいかもしれませんが、単にキーワードを含めるのを忘れていました)。

オーバーライドするプロパティ宣言には、sealed修飾子を含めることができます。この修飾子を使用すると、派生クラスがプロパティをさらにオーバーライドできなくなります。封印されたプロパティのアクセサも封印されます。

101
CharithJ

どのメソッドもオーバーライド可能(= virtual)でもそうでなくてもかまいません。決定は、メソッドを定義する人によって行われます。

class Person
{
    // this one is not overridable (not virtual)
    public String GetPersonType()
    {
        return "person";
    }

    // this one is overridable (virtual)
    public virtual String GetName()
    {
        return "generic name";
    }
}

これで、オーバーライド可能なメソッドをオーバーライドできます。

class Friend : Person
{
    public Friend() : this("generic name") { }

    public Friend(String name)
    {
        this._name = name;
    }

    // override Person.GetName:
    public override String GetName()
    {
        return _name;
    }
}

ただし、GetPersonTypeメソッドは仮想ではないため、オーバーライドできません。

それらのクラスの2つのインスタンスを作成しましょう。

Person person = new Person();
Friend friend = new Friend("Onotole");

非仮想メソッドGetPersonTypeFiendインスタンスによって呼び出されると、実際にはPerson.GetPersonTypeが呼び出されます。

Console.WriteLine(friend.GetPersonType()); // "person"

仮想メソッドGetNameFriendインスタンスによって呼び出されると、Friend.GetNameが呼び出されます:

Console.WriteLine(friend.GetName()); // "Onotole"

仮想メソッドGetNamePersonインスタンスによって呼び出されると、Person.GetNameが呼び出されます:

Console.WriteLine(person.GetName()); // "generic name"

非仮想メソッドが呼び出された場合、メソッド本体は検索されません-コンパイラは、呼び出される必要がある実際のメソッドをすでに知っています。一方、仮想メソッドコンパイラでは、どのメソッドを呼び出すかを確認できず、メソッドが呼び出されるインスタンスのタイプから開始して、クラス階層の実行時に下から上に検索されます:for friend.GetName it Friendクラスから始まり、すぐにそれを見つけます。person.GetNameクラスの場合、Personから始まり、そこで見つけます。

サブクラスを作成し、仮想メソッドをオーバーライドし、階層内でこれ以上オーバーライドしたくない場合があります-そのためにsealed overrideを使用します(メソッドをオーバーライドする最後のユーザーであると言います):

class Mike : Friend
{
    public sealed override String GetName()
    {
        return "Mike";
    }
}

しかし、友人のマイクが性別を変えて名前をアリスに変更する場合があります:)元のコードを変更するか、代わりにマイクをサブクラスにすることができます:

class Alice : Mike
{
    public new String GetName()
    {
        return "Alice";
    }
}

ここでは、同じ名前の完全に異なるメソッドを作成します(2つあります)。どのメソッドがいつ呼び出されますか?それはあなたがそれをどのように呼ぶかに依存します:

Alice alice = new Alice();
Console.WriteLine(alice.GetName());             // the new method is called, printing "Alice"
Console.WriteLine(((Mike)alice).GetName());     // the method hidden by new is called, printing "Mike"

Aliceの観点から呼び出す場合はAlice.GetNameを呼び出し、Mikeの観点から呼び出す場合はMike.GetNameを呼び出します。ここではランタイムのルックアップは行われません-両方のメソッドが非仮想であるためです。

newメソッドは、いつでも作成できます-非表示にしているメソッドが仮想であるかどうか。

これはプロパティとイベントにも適用されます-それらは下のメソッドとして表されます。

35
Loki Kriasus

デフォルトでは、virtualまたはabstractとして宣言されていない限り、派生クラスでメソッドをオーバーライドすることはできません。 virtual呼び出す前に新しい実装を確認するを意味し、abstractは同じことを意味しますが、すべての派生クラスでオーバーライドされることが保証されています。また、基本クラスは他の場所で再定義されるため、基本クラスでの実装は必要ありません。

上記の例外は、new修飾子です。 virtualまたはabstractとして宣言されていないメソッドは、派生クラスでnew修飾子を使用して再定義できます。メソッドが基本クラスで呼び出されると、基本メソッドが実行され、派生クラスで呼び出されると、新しいメソッドが実行されます。すべてのnewキーワードを使用すると、クラス階層で2つのメソッド同じ名前を使用できます。

最後に、sealed修飾子はvirtualメソッドのチェーンを破壊し、それらを再びオーバーライドできないようにします。これはあまり使用されませんが、オプションはあります。それぞれ前のクラスから派生した3つのクラスのチェーンを使用するとより意味があります

A -> B -> C

Avirtualまたはabstractメソッドがある場合、つまりoverriddenBである場合、Cが再び変更するのを防ぐこともできます。 sealedBを宣言します。

sealedclassesでも使用されます。これは、一般的にこのキーワードに出会う場所です。

これがお役に立てば幸いです。

17
ja72
 public class Base 
 {
   public virtual void SomeMethod()
   {
     Console.WriteLine("B");
   }
  }

public class Derived : Base
{
   //Same method is written 3 times with different keywords to explain different behaviors. 


   //This one is Simple method
  public void SomeMethod()
  {
     Console.WriteLine("D");
  }

  //This method has 'new' keyword
  public new void SomeMethod()
  {
     Console.WriteLine("D");
  }

  //This method has 'override' keyword
  public override void SomeMethod()
  {
     Console.WriteLine("D");
  }
}

今最初のもの

 Base b=new Base();
 Derived d=new Derived();
 b.SomeMethod(); //will always write B
 d.SomeMethod(); //will always write D

キーワードはすべてポリモーフィズムに関するものです

 Base b = new Derived();
  1. 基本クラスでvirtualを使用し、Derivedでオーバーライドすると、D(多態性)が得られます。
  2. overridevirtualなしでBaseを使用すると、エラーが発生します。
  3. 同様に、virtualを使用してメソッド(オーバーライドなし)を記述すると、警告とともに 'B'が書き込まれます(ポリモーフィズムは行われないため)。
  4. 上記のような警告を隠すには、newの単純なメソッドの前にDerivedを記述します。
  5. newキーワードは別の話です。同じ名前のプロパティが基本クラスにあることを伝える警告を単に非表示にします。
  6. virtualまたはnewは、 新しい修飾子 を除いて同じです。

  7. newoverrideは、同じメソッドまたはプロパティの前では使用できません。

  8. sealedをクラスまたはメソッドの前にロックすると、派生クラスで使用するためにロックされ、コンパイル時エラーが発生します。
8
Charlie