クラスの継承②
動作の書き換え
オーバーライド
overrideキーワードを付与することで、継承元のメソッドと同名・同シグネチャのメソッドを継承先で定義(オーバーライド)することができます。
これに伴い、継承元のメソッドは外部から使用できなくなります。
オーバーライド可能なメソッドには以下のキーワードを付与します。
virtual
継承元で規定の動作を定義しつつ、継承先でのオーバーライドを許容する場合に付与します。
継承先でオーバーライドしない場合、常に継承元のメソッドが呼び出されます。
public virtual void Fuga() { // 規定の動作を実装 }
abstract
継承元で規定の動作を定義せず、継承先でのオーバーライド必須とする場合に付与します。
abstractなメソッドは本体を記述しません。
public abstract void Fuga();
継承元で規定の動作を定義せず、継承先に委ねるケースで使用します。
base呼び出し
baseキーワードを用いて継承元のメソッドを呼び出すことができます。
public override void Fuga() { base.Fuga(); }
継承元の処理を呼び出しつつ、継承先独自の処理も行いたいケースなどで有用です。
new
newキーワードを付与することで、継承元のメソッドを上書きできます。
継承元の同名・同シグネチャのメソッドにvirtualキーワードが付与されていなくても可能です。
ただし、継承元でvirtualキーワード付与されていないということは、継承先で動作を書き換えられることを想定していないということなので、使用には注意が必要です。
オーバーライドとnewの動作の違い
オーバーライドされたメソッドの呼び出しは、呼び出し時の型に関係なく、インスタンス本来の型で決定されます。
newされたメソッドの呼び出しは、インスタンス本来の型に関係なく、呼び出し時の型で決定されます。
以下のようなクラスを用意して説明します。
public class HogeBase1 { public virtual void Fuga() => Console.WriteLine("HogeBase1"); public void Piyo() => Console.WriteLine("HogeBase1"); } public class HogeBase2 { public override void Fuga() => Console.WriteLine("HogeBase2"); public new void Piyo() => Console.WriteLine("HogeBase2"); } public class Hoge { public override void Fuga() => Console.WriteLine("Hoge"); public new void Piyo() => Console.WriteLine("Hoge"); }
Fugaメソッドについて、実体がHogeの場合は常に"Hoge"が出力されます。
var hoge = new Hoge(); hoge.Fuga(); ((HogeBase2)hoge).Fuga(); ((HogeBase1)hoge).Fuga();
Piyoメソッドについて、呼び出し時の型で出力結果が変わります。
var hoge = new Hoge(); hoge.Piyo(); ((HogeBase2)hoge).Piyo(); ((HogeBase1)hoge).Piyo();
実行結果はこうなります。
Hoge HogeBase2 HogeBase1
継承の抑止
sealedキーワードを付与することで、継承を抑止できます。
メソッドにsealedキーワードを付与した場合、継承先クラスで当該メソッドをオーバーライドできなくなります。
基底クラスでsealedを付与することはありません。
多段継承していて、中間層のクラスで継承を止めたい場合に有効です。
public class HogeBase1 { public virtual Fuga() {} } public class HogeBase2 : HogeBase1 { // 以降の継承を抑止 public override sealed Fuga() {} } public class Hoge { // コンパイルエラー public override Fuga() {} }
クラスにsealedキーワード付与した場合、当該クラスを継承したクラスを定義できなくなります。
public sealed class HogeBase {} // コンパイルエラー public class Hoge : HogeBase {}