Virtual と Override キーワード
CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 21/70。
派生クラスがメソッドを継承したとき、そのメソッドの動作を完全に変更したい場合があります。virtualキーワードとoverrideキーワードを組み合わせることで、制御された方法でこれを実現できます。
派生クラスが実装を置き換えられることを示すには、基底クラスのメソッドにvirtualを指定します。
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Some sound");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}派生クラスのoverrideキーワードは、基底クラスのメソッドを意図的に置き換えることをコンパイラに伝えます。基底クラスのメソッドにvirtualが付いていない場合、overrideは使用できません。
基底クラスの参照を使って派生オブジェクトを保持すると、その真価が発揮されます。
Animal myPet = new Dog();
myPet.Speak(); // 出力: Woof!myPet は Animal として宣言されていますが、C# は Speak() の Dog 版を呼び出します。これは実行時ポリモーフィズムと呼ばれます。実際に呼び出されるメソッドは、変数の宣言された型ではなく、オブジェクトの実際の型によって決まります。
動作を完全に置き換えるのではなく拡張したい場合は、オーバーライド内でbase.Speak()を使って基本の実装に引き続きアクセスできます。
チャレンジ
簡単virtualキーワードとoverrideキーワードを使って、派生クラスが基底クラスの動作を完全にオーバーライドできることを示すとともに、実行時ポリモーフィズムの威力も紹介する通知システムを作りましょう。
コードを整理するために、3つのファイルを作成します。
Notification.cs:Alerts名前空間にNotificationクラスを定義します。この基底クラスは、汎用的な通知を表し、Messageプロパティ(string)と、それを設定するコンストラクターを持ちます。"Sending notification: {Message}"を返すvirtualメソッドSend()を含めます。EmailNotification.cs:Alerts名前空間に、Notificationを継承するEmailNotificationクラスを定義します。Recipientプロパティ(string)と、messageとrecipientの両方を受け取るコンストラクターを追加します(baseを使ってmessageを渡します)。Send()メソッドをオーバーライドし、"Emailing {Recipient}: {Message}"を返すようにします。SmsNotification.cs:Alerts名前空間に、同じくNotificationを継承するSmsNotificationクラスを定義します。PhoneNumberプロパティ(string)と、messageとphone numberの両方を受け取るコンストラクターを追加します。Send()をオーバーライドし、"SMS to {PhoneNumber}: {Message}"を返すようにします。Program.cs: メインファイルで、入力値を使って3種類すべての通知のインスタンスを作成します。次に、3つすべてをNotification型の変数に格納し、それぞれに対してSend()を呼び出して、実行時ポリモーフィズムを示します。
入力は4つ受け取ります。
- 通知メッセージ
- メールの宛先アドレス
- SMS用の電話番号
基底クラスの参照として格納した各通知に対してSend()を呼び出し、その結果を出力します。
{base notification Send() result}
{email notification Send() result}
{sms notification Send() result}たとえば、入力がMeeting at 3pm、alice@email.com、555-1234の場合、出力は次のようになります。
Sending notification: Meeting at 3pm
Emailing alice@email.com: Meeting at 3pm
SMS to 555-1234: Meeting at 3pm3つすべてがNotification型の変数に格納されていても、それぞれが独自にオーバーライドしたSend()を呼び出すことに注目してください。これが実行時ポリモーフィズムの働きです。
自分で試してみよう
using System;
using Alerts;
class Program
{
public static void Main(string[] args)
{
// 入力を読み取る
string message = Console.ReadLine();
string email = Console.ReadLine();
string phone = Console.ReadLine();
// TODO: 3種類すべての通知型のインスタンスを作成する
// ポリモーフィズムを示すため、それらをNotification型の変数に格納する
// TODO: 基底のNotificationを作成する
Notification baseNotification;
// TODO: EmailNotificationを作成する(Notification型の変数に格納する)
Notification emailNotification;
// TODO: SmsNotificationを作成する(Notification型の変数に格納する)
Notification smsNotification;
// TODO: それぞれでSend()を呼び出し、結果を出力する
// 実行時ポリモーフィズムにより、オーバーライドされたメソッドが呼び出される
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: C#オンラインコンパイラ