OOPのいくつかの概念の間でかなり混乱しています:virtual
、override
、new
およびsealed override
。誰でも違いを説明できますか?
派生クラスメソッドを使用する場合は、override
キーワードを使用して、派生クラスで基本クラスメソッドをオーバーライドできることは明らかです。しかし、new
とsealed override
についてはわかりません。
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修飾子を含めることができます。この修飾子を使用すると、派生クラスがプロパティをさらにオーバーライドできなくなります。封印されたプロパティのアクセサも封印されます。
どのメソッドもオーバーライド可能(= 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");
非仮想メソッドGetPersonType
がFiend
インスタンスによって呼び出されると、実際にはPerson.GetPersonType
が呼び出されます。
Console.WriteLine(friend.GetPersonType()); // "person"
仮想メソッドGetName
がFriend
インスタンスによって呼び出されると、Friend.GetName
が呼び出されます:
Console.WriteLine(friend.GetName()); // "Onotole"
仮想メソッドGetName
がPerson
インスタンスによって呼び出されると、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
メソッドは、いつでも作成できます-非表示にしているメソッドが仮想であるかどうか。
これはプロパティとイベントにも適用されます-それらは下のメソッドとして表されます。
デフォルトでは、virtual
またはabstract
として宣言されていない限り、派生クラスでメソッドをオーバーライドすることはできません。 virtual
は呼び出す前に新しい実装を確認するを意味し、abstract
は同じことを意味しますが、すべての派生クラスでオーバーライドされることが保証されています。また、基本クラスは他の場所で再定義されるため、基本クラスでの実装は必要ありません。
上記の例外は、new
修飾子です。 virtual
またはabstract
として宣言されていないメソッドは、派生クラスでnew
修飾子を使用して再定義できます。メソッドが基本クラスで呼び出されると、基本メソッドが実行され、派生クラスで呼び出されると、新しいメソッドが実行されます。すべてのnew
キーワードを使用すると、クラス階層で2つのメソッド同じ名前を使用できます。
最後に、sealed
修飾子はvirtual
メソッドのチェーンを破壊し、それらを再びオーバーライドできないようにします。これはあまり使用されませんが、オプションはあります。それぞれ前のクラスから派生した3つのクラスのチェーンを使用するとより意味があります
A -> B -> C
A
にvirtual
またはabstract
メソッドがある場合、つまりoverridden
のB
である場合、C
が再び変更するのを防ぐこともできます。 sealed
でB
を宣言します。
sealed
はclasses
でも使用されます。これは、一般的にこのキーワードに出会う場所です。
これがお役に立てば幸いです。
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();
virtual
を使用し、Derived
でオーバーライドすると、D(多態性)が得られます。override
でvirtual
なしでBase
を使用すると、エラーが発生します。virtual
を使用してメソッド(オーバーライドなし)を記述すると、警告とともに 'B'が書き込まれます(ポリモーフィズムは行われないため)。new
の単純なメソッドの前にDerived
を記述します。new
キーワードは別の話です。同じ名前のプロパティが基本クラスにあることを伝える警告を単に非表示にします。virtual
またはnew
は、 新しい修飾子 を除いて同じです。
new
とoverride
は、同じメソッドまたはプロパティの前では使用できません。
sealed
をクラスまたはメソッドの前にロックすると、派生クラスで使用するためにロックされ、コンパイル時エラーが発生します。