オーバーライド
仮想メソッドによる型と実体の分離
通常、インスタンスの参照からメンバにアクセスすると、参照の型(変数のクラス型)からメンバにアクセスします。従って、前述したように派生クラスによって基本クラスのメンバが隠ぺいされている場合、どのメンバが呼び出されるかは型によって決定されます。これは、例えばメソッドにおいても同じです。
class A { public void M() { System.Console.WriteLine("A.M()"); } } class B : A { public new void M() { System.Console.WriteLine("B.M()"); } }
上のコードのような M() メソッドを宣言する A クラスと、A クラスを継承する B クラスがあり、B クラスで A クラスの M() メソッドを隠ぺいしています。この場合 B クラスのインスタンスには A クラスの M() メソッドと B クラスの M() メソッドが 2 つあり、どちらのメソッドを呼び出すかは型によって決定されます。
B objB = new B(); A objA = objB; objA.M(); //A.M objB.M(); //B.M
上記のように B クラスのインスタンスを作成し、この参照を B クラス型の変数 objB と A クラス型の変数 objA に代入した場合、呼び出されるメソッドは変数の型によって決定されます。
メソッドに対しては、基本クラスのメソッドを隠ぺいするのではなく、同一シグネチャのメソッドの実装を常に 1 つに限定することができます。つまり、変数の型ではなく、インスタンスの型によって呼び出すメソッドを決定します。このような仕組みをオーバーライド(override)と呼びます。
オーバーライドされたメソッドは、変数の型ではなく、変数が参照するインスタンスに応じて呼び出されます。これは、メソッドを呼び出すコードを変更することなく、参照するインスタンスによって実行するメソッドが切り替えられることを意味しており、オブジェクト指向の抽象的な設計に欠かせない機能です。オーバーライドなどによって実現される、同一のコードが状態に応じて多様な振る舞いを取る性質のことを多態性(Polymorphism)と呼びます。英読みをカタカナにしてポリモーフィズムと呼ぶことも多いです。
派生クラスでメソッドをオーバーライドするには、対象のメソッドが仮想メソッド(virtual method)でなければなりません。仮想メソッドとは virtual 修飾子(virtual modifier)によって宣言されたメソッドで、派生クラスによってメソッドの実装を取り換えることを許容します。これに対して、通常のメソッドのことを非仮想メソッド(non-virtual method)とも呼びます。
class A { public virtual void M() { System.Console.WriteLine("A.M()"); } }
上記の A クラスで宣言された M() メソッドは virtual 修飾子を含むため仮想メソッドです。
仮想メソッドを派生クラスでオーバーライドするには、メソッド宣言で override 修飾子(override modifier)を指定します。override 修飾子で宣言されたメソッドのことをオーバーライドメソッド(override method)と呼びます。
class B : A { public override void M() { System.Console.WriteLine("B.M()"); } }
上記の B クラスは A クラスを継承し、A クラスの M() メソッドをオーバーライドします。これによって、B クラスのインスタンスへの参照から M() メソッドを呼び出した場合、 参照の型が何であっても(例えば A 型の変数からでも)B クラスの M() メソッドが呼び出されます。
class PrettyCure { public virtual void Action() { System.Console.WriteLine("プリキュア!"); } } class HeartCatchPreCure : PrettyCure { public override void Action() { System.Console.WriteLine("オープンマイハート!"); } } class SmilePreCure : PrettyCure { public override void Action() { System.Console.WriteLine("スマイルチャージ!"); } } class Test { public static void Main(string[] args) { PrettyCure precure = new PrettyCure(); precure.Action(); precure = new HeartCatchPreCure(); precure.Action(); precure = new SmilePreCure(); precure.Action(); } }
コード1の PrettyCure クラスは virtual 修飾子で修飾された Action() メソッドを宣言しています。Action() メソッドは仮想メソッドなので、派生クラスでオーバーライドしてメソッドを取り換えることができます。PrettyCure クラスを継承する HeartCatchPreCure クラスと SmilePreCure クラスで、それぞれ Action() メソッドをオーバーライドしています。
Main() メソッド内で各クラスのインスタンスを生成し、すべてを基本クラスである PrettyCure 型の precure 変数に代入し、ここから Action() メソッドを呼び出します。実行結果のように、変数の型ではなく参照しているインスタンス(実際の型)のメソッドが呼び出されます。常に precure 変数から Action() メソッドを呼び出していますが、変数が参照するインスタンスによって呼び出されるメソッドが変化しています。