オーバーライドは、基底クラスと同じシグネチャのメソッドを派生クラスで定義することができます。
これは、new 演算子を使用した隠蔽と似ていますが、オーバーライドを使用すると、継承されたメンバの実装を拡張することができます。
- class クラス名1 // 基底クラス
{
public virtual void メソッド名() { } // オーバーライドされるメソッドには virtual を指定します。
}
class クラス名2 : 基底クラス名
{
public override void メソッド名() { } // 基底クラスと同じシグネチャのメソッドでなければいけません。
}
基底クラスで virtual を指定することにより、派生クラスでオーバーライドをすることができるようになります。
この virtual を指定したメソッドを仮想メソッドと呼びます。
virtual 修飾子は、static、abstract、および override の各修飾子と一緒には使用できません。
仮想プロパティは抽象メソッドと同様に動作しますが、宣言の構文および呼び出しの構文に相違があります。
override 修飾子は、継承したメソッド、プロパティ、インデクサ、またはイベントの抽象実装(abstract)や仮想実装(virtual)を拡張したり修飾したりする際に必要です。
override メソッドは、基底クラスから継承されたメソッドの新しい実装を用意します。
オーバーライドされた基底メソッドは、override メソッドと同じシグネチャを持つ必要があります。
このメソッドには、修飾子 new、static、virtual、または abstract は使用できません。
それでは、オーバーロードと new 演算子を指定したときの違いを以下のプログラムで確認して見ます。
//サンプル37
using System;
namespace ConsoleApplication
{
public class A
{
public void Show1()
{
Console.WriteLine("A.Show1");
}
public virtual void Show2()
{
Console.WriteLine("A.Show2");
}
}
public class B : A
{
public new void Show1()
{
Console.WriteLine("B.Show1");
}
public override void Show2()
{
Console.WriteLine("B.Show2");
}
static void Main()
{
B objB = new B(); // ①
objB.Show1(); // 基底クラスのメソッドは new 修飾子で隠蔽されているため、B.Show1が呼び出されます。
objB.Show2(); // オーバーライドされた B.Show2 が呼び出されます。
A objA = new B(); //
objA.Show1(); // ②
objA.Show2(); //
}
}
}
実行結果
①に関しては、特に問題はなさそうですね。
②は結果を見てみると、new 演算子のときとオーバーライドの時とでは結果が違いますね。
まず、 A objA = new B(); の部分を見てみますと、変数 obj の型は A であるのに対し、インスタンスの型は B でありますね。
これは、派生クラス B は基底クラス A のインスタンスを持つため、派生クラスのインスタンスは基底クラス型に暗黙的にキャストできるため
構文上は問題ありません。
このような状態を アップキャスト と呼びます。
アップキャストされると、派生クラスで追加した機能は削除され、基底クラスだけの機能を実装することができます。
このことを踏まえて objA.Show1(); を見てみましょう。
これは、現在アップキャストされた状態にあるので基底クラスのメソッドが呼び出されます。
A.Show1 と表示されるのは想定通りですね。
しかし、 objA.Show2(); は派生クラスのメソッドを呼び出しています。
これが、new 演算子とオーバーライドの違いです。
virtual を指定すると、指定されたメソッドは仮想化され、その実体は実行時までわからないことをコンパイラに通知します。
仮想メソッドを実体化するには、派生クラスでoverride を指定したメソッドを呼び出します。
このように、どのメソッドが呼び出されるかを実行時に決定する方法をオーバーライドと呼びます。