1. はじめに
例えば、動物園の動物管理アプリがあるとしよう(前の講義で作り始めたやつね)。Animalっていうベースクラスを作って、MakeSoundメソッドで全ての動物が何かしらの音を「出せる」ようにしたんだ。
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("動物が音を出す。");
}
}
でも、ライオンとオウムじゃ全然違う音を出すよね。「動物が音を出す」じゃ全然足りない。ここでメソッドのオーバーライドの出番!それぞれの子クラスが自分なりの音を出せるようにしたいよね。
2. シンタックス:overrideの使い方
基本ルール
- メソッドをオーバーライドしたいなら、ベースクラスでvirtual(またはabstract)で宣言しとく必要がある。
- 派生クラスでは、同じシグネチャのメソッドの前にoverrideを付ける。
例:ライオンとオウム
public class Lion : Animal
{
public override void MakeSound()
{
Console.WriteLine("ガオー!");
}
}
public class Parrot : Animal
{
public override void MakeSound()
{
Console.WriteLine("ポッカはバカだ!");
}
}
これで、動物のリストを作ってそれぞれにMakeSoundを呼ぶと、みんな自分の「声」でしゃべるようになるよ:
Animal[] zoo = new Animal[]
{
new Lion(),
new Parrot(),
new Animal()
};
foreach (var animal in zoo)
{
animal.MakeSound();
}
// 出力:
// ガオー!
// ポッカはバカだ!
// 動物が音を出す。
3. バーチャルメソッドテーブル(v-table)
virtualやoverrideを使うと、コンパイラはクラス用に特別な「バーチャルメソッドテーブル(v-table)」を作るんだ。
ベースクラスの参照でメソッドを呼ぶとき、CLRはこのテーブルを見て「このメソッド、子クラスでオーバーライドされてない?」ってチェックする。もしオーバーライドされてたら、子クラスのバージョンが呼ばれる。
これが「レイトバインディング(late binding)」とかダイナミックポリモーフィズムの「魔法」ってやつ!
4. まとめてみよう:アプリをさらに進化させる
もう一つクラスを追加してみよう:
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("ワンワン!");
}
}
そして、動物管理ミニプログラムで使ってみる:
Animal[] zoo = new Animal[]
{
new Lion(),
new Parrot(),
new Dog()
};
foreach (var animal in zoo)
{
animal.MakeSound();
}
これでコードの拡張性もメンテ性もめっちゃ上がるし、新しい動物を追加するのも超楽しくなるよ!
5. プロパティのオーバーライドとoverrideの戻り値
オーバーライドできるのはメソッドだけじゃない。バーチャルなプロパティやインデクサもオーバーライドできるよ。
public class Animal
{
public virtual string Name { get; set; } = "動物";
}
public class Lion : Animal
{
public override string Name { get; set; } = "ライオン";
}
こうやって、特定の種類ごとに情報を細かくできて便利!
6. baseでベースの実装を呼ぶ
ベースメソッドの動作を完全に置き換えるんじゃなくて、ちょっとだけ拡張したい時もあるよね。
そんな時は、オーバーライドしたメソッドの中でbaseを使ってベースの実装を呼び出せばOK。
public class Parrot : Animal
{
public override void MakeSound()
{
base.MakeSound(); // "動物が音を出す。"
Console.WriteLine("ポッカはバカだ!");
}
}
この場合、オウムはまず「動物園っぽい」音を出してから、自分の決め台詞「ポッカはバカだ!」を言うよ。
7. 実践で役立つ場面
overrideの仕組みを理解してると、ガチなC#プロジェクトでOOPを使う時にめっちゃ役立つよ。
- うちの動物園モデル? もうやったね。
- UI用のカスタムコントロール作成: 標準のビジュアルメソッドをオーバーライドして自分のロジックを追加できる。
- 柔軟なビジネスロジック: ベースクラスで「骨組み」を作って、具体的な動作は子クラスで実装できる。
- テストやモック: ユニットテスト用に子クラスでメソッドを簡単に「差し替え」できる。
- プラグインや拡張: インターフェースや抽象ベースクラス+複数実装、全部正しいオーバーライドのおかげで動く!
8. sealed overrideとチェーン内のバーチャルメソッド
もし「これ以上このメソッドをオーバーライドされたくない!」って時は、sealed overrideを使えばOK:
public class Base
{
public virtual void Foo() { }
}
public class Middle : Base
{
public sealed override void Foo() { }
}
public class Last : Middle
{
public override void Foo() {} // エラー — オーバーライドできない!
}
こうやって、システムの正しい動作にとって重要なところでオーバーライドの連鎖を「封じる」ことができるよ。
9. 新しいメソッド(newキーワード)
ベースクラスのメソッドがvirtualじゃないけど、同じシグネチャで子クラスにメソッドを作りたい時もあるよね。
そんな時はnewキーワードを使う。でもこれはポリモーフィズムじゃなくて、ただの「隠蔽」だよ:
public class Animal
{
public void MakeSound()
{
Console.WriteLine("私は動物だ!");
}
}
public class Cat : Animal
{
public new void MakeSound()
{
Console.WriteLine("私はネコだよ!");
}
}
Animal animal = new Cat();
animal.MakeSound(); // "私は動物だ!"
Cat cat = new Cat();
cat.MakeSound(); // "私はネコだよ!"
ここでは、呼び出し時の変数の型が全てを決める!だから、本当のダイナミックポリモーフィズムをやりたいなら、virtual+overrideの組み合わせを使おう。
10. メソッドオーバーライドでよくあるミス
ミスその1: virtual、abstract、overrideで宣言されてないメソッドをオーバーライドしようとする。
C#では普通のメソッドはオーバーライドできないよ。ベースクラスのメソッドに特別な修飾子(virtual、abstract、override)が付いてないと、派生クラスでoverrideを書いてもコンパイルエラーになる。
ミスその2: メソッドのシグネチャが一致してない。
本当にオーバーライドしたいなら、名前・戻り値の型・パラメータがベースクラスのメソッドと完全一致してないとダメ。ちょっとでも違う(例えばパラメータの型が違う)と、新しいメソッドが作られるだけで、オーバーライドにはならないよ。
ミスその3: override修飾子を付け忘れる。
ベースクラスと同じシグネチャのメソッドを定義しても、overrideを付けなかったら、コンパイラはそれをオーバーライドとは見なさない。これはメソッドの隠蔽(別で説明するよ)って呼ばれる。こうなると、ベース型の変数でメソッドを呼ぶと、ベースのメソッドが呼ばれて、思った通りに動かないから注意!
GO TO FULL VERSION