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#はDogバージョンのSpeak()を呼び出します。これは実行時ポリモーフィズムと呼ばれます。実際に呼び出されるメソッドは、変数の宣言された型ではなく、オブジェクトの実際の型に依存します。
振る舞いを完全に置き換えるのではなく拡張したい場合は、オーバーライド内で base.Speak() を使用して基本の実装にアクセスすることができます。
チャレンジ
簡単virtual キーワードと override キーワードを使用して、派生クラスが基底クラスの動作を完全にオーバーライドする方法を示す通知システムを構築しましょう。同時に、実行時ポリモーフィズムの強力さも確認します。
コードを整理するために、3つのファイルを作成します。
Notification.cs:Alerts名前空間にNotificationクラスを定義します。この基底クラスは、Messageプロパティ(文字列)とそれを設定するコンストラクタを持つ汎用的な通知を表します。"Sending notification: {Message}"を返すvirtualメソッドSend()を含めてください。EmailNotification.cs:Notificationを継承するEmailNotificationクラスをAlerts名前空間に定義します。Recipientプロパティ(文字列)と、メッセージと受信者の両方を受け取るコンストラクタを追加します(メッセージを渡すにはbaseを使用してください)。Send()メソッドをオーバーライドして、"Emailing {Recipient}: {Message}"を返すようにします。SmsNotification.cs: 同じくNotificationを継承するSmsNotificationクラスをAlerts名前空間に定義します。PhoneNumberプロパティ(文字列)と、メッセージと電話番号の両方を受け取るコンストラクタを追加します。Send()をオーバーライドして、"SMS to {PhoneNumber}: {Message}"を返すようにします。Program.cs: メインファイルで、入力値を使用して3つの通知タイプすべてのインスタンスを作成します。次に、これら3つすべてをNotification型の変数に格納し、それぞれに対してSend()を呼び出すことで、実行時ポリモーフィズムを実証します。
4つの入力を受け取ります(実際には以下の3項目です):
- 通知メッセージ
- メールの受信者アドレス
- 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() を呼び出していることに注目してください。これが実行時ポリモーフィズムの仕組みです!
チートシート
基底クラスで virtual キーワードを使用して、派生クラスがオーバーライドできるメソッドをマークします:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Some sound");
}
}派生クラスで override キーワードを使用して、基底クラスのメソッドの実装を置き換えます:
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}実行時ポリモーフィズムは、基底クラスの参照が派生オブジェクトを保持しているときに発生します。実際に呼び出されるメソッドは、変数の宣言された型ではなく、オブジェクトの実際の型に依存します:
Animal myPet = new Dog();
myPet.Speak(); // 出力: Woof!base を使用して、オーバーライド内で基底クラスの実装にアクセスできます:
public override void Speak()
{
base.Speak(); // 基底クラスのバージョンを呼び出します
Console.WriteLine("Woof!");
}自分で試してみよう
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()を呼び出し、結果を出力する
// 実行時のポリモーフィズムにより、オーバーライドされたメソッドが呼び出される
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。