アップキャストとダウンキャスト
CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 29/70。
継承階層を扱う際、基本型と派生型の間で変換を行う必要がよくあります。アップキャスト(Upcasting)は派生型をその基本型に変換し、一方でダウンキャスト(Downcasting)はその逆を行います。
アップキャスティングは暗黙的であり、派生クラスは基底クラスが持つすべての要素を備えていることが保証されているため、常に安全です:
Dog myDog = new Dog();
Animal animal = myDog; // アップキャスティング - 暗黙的、常に成功しますダウンキャストには明示的なキャストが必要であり、オブジェクトが実際にキャスト先の型ではない場合、実行時に失敗する可能性があります:
Animal animal = new Dog();
Dog dog = (Dog)animal; // ダウンキャスト - 実際にはDogなので動作します
Animal cat = new Cat();
Dog wrong = (Dog)cat; // InvalidCastExceptionをスローします!安全にダウンキャストするには、最初に is キーワードを使用して型を確認するか、例外をスローする代わりに null を返す as を使用します。
if (animal is Dog d)
{
d.Bark(); // 安全 - チェックが通った場合のみ d が利用可能です
}
Dog maybeDog = animal as Dog;
if (maybeDog != null)
{
maybeDog.Bark();
}モダンなC#では、型チェックとキャストを1つのステップで組み合わせることで、コードをより安全かつクリーンにできるため、isパターンマッチングのアプローチが好まれます。
チャレンジ
簡単継承階層内のオブジェクトを柔軟に操作できるアップキャストとダウンキャストを実証する、車両検査システムを構築しましょう。さまざまな車両タイプを作成し、特定の車両の種類を安全に識別して対話するコードを記述します。
コードは4つのファイルに分けて構成します:
Vehicle.cs:Inspection名前空間にベースとなるVehicleクラスを定義します。すべての車両は、コンストラクタを通じて設定されるLicensePlateプロパティ(string)を持ちます。"Vehicle: {LicensePlate}"を返すGetBasicInfo()というメソッドを含めてください。Car.cs:Inspection名前空間に、Vehicleを継承するCarクラスを定義します。車には追加のNumberOfDoorsプロパティ(int)があります。コンストラクタはライセンスプレートとドア数の両方を受け取ります。"{LicensePlate} honks: Beep beep!"を返すHonk()というメソッドを追加してください。Motorcycle.cs:Inspection名前空間に、Vehicleを継承するMotorcycleクラスを定義します。オートバイにはHasSidecarプロパティ(bool)があります。コンストラクタはライセンスプレートとサイドカーの有無を受け取ります。"{LicensePlate} revs: Vroom!"を返すRev()というメソッドを追加してください。Program.cs: メインファイルでは、車両を作成し、アップキャストと安全なダウンキャストの両方を練習します。入力値を使用してCarとMotorcycleを作成し、両方をVehicle参照の配列に格納します(アップキャスト)。配列をループし、各車両についてその基本情報を出力します。次に、isキーワードとパターンマッチングを使用して安全にダウンキャストします。もしCarであればHonk()を呼び出し、MotorcycleであればRev()を呼び出します。
4つの入力を受け取ります:
- 車のライセンスプレート
- ドアの数
- オートバイのライセンスプレート
- サイドカーの有無(
trueまたはfalse)
配列内の各車両について、次の形式で出力を表示してください:
{GetBasicInfo() result}
{Honk() or Rev() result based on actual type}例えば、入力がABC-123、4、XYZ-789、falseの場合、出力は次のようになります:
Vehicle: ABC-123
ABC-123 honks: Beep beep!
Vehicle: XYZ-789
XYZ-789 revs: Vroom!すべての車両がVehicle参照として格納されている(アップキャスト)にもかかわらず、ダウンキャストのためのisパターンマッチング構文を使用して、それらの真の型を安全に発見し、型固有のメソッドにアクセスできることに注目してください!
チートシート
継承階層を扱う際、アップキャストは派生型を基本型に変換し、ダウンキャストは基本型を派生型に変換します。
アップキャストは暗黙的であり、常に安全です:
Dog myDog = new Dog();
Animal animal = myDog; // アップキャスト - 暗黙的、常に動作しますダウンキャストは明示的なキャストが必要であり、実行時に失敗する可能性があります:
Animal animal = new Dog();
Dog dog = (Dog)animal; // ダウンキャスト - 実際にはDogであるため動作します
Animal cat = new Cat();
Dog wrong = (Dog)cat; // InvalidCastExceptionをスローします!パターンマッチングを伴う is キーワードを使用した安全なダウンキャスト(推奨):
if (animal is Dog d)
{
d.Bark(); // 安全 - dはチェックに合格した場合のみ使用可能です
}as キーワードを使用した安全なダウンキャスト(キャストに失敗した場合は null を返します):
Dog maybeDog = animal as Dog;
if (maybeDog != null)
{
maybeDog.Bark();
}自分で試してみよう
using System;
using Inspection;
class Program
{
public static void Main(string[] args)
{
// 入力を読み込む
string carPlate = Console.ReadLine();
int numberOfDoors = Convert.ToInt32(Console.ReadLine());
string motorcyclePlate = Console.ReadLine();
bool hasSidecar = Convert.ToBoolean(Console.ReadLine());
// TODO: carPlate と numberOfDoors を使用して Car オブジェクトを作成する
// TODO: motorcyclePlate と hasSidecar を使用して Motorcycle オブジェクトを作成する
// TODO: 両方の車両を含む Vehicle 参照の配列を作成する(アップキャスティング)
// TODO: 配列をループで回し、各車両に対して以下を行う:
// - GetBasicInfo() の結果を出力する
// - パターンマッチングを伴う 'is' キーワードを使用して、安全にダウンキャストする:
// - もし Car であれば、Honk() を呼び出して出力する
// - もし Motorcycle であれば、Rev() を呼び出して出力する
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
5ポリモーフィズムとインターフェース
コンパイル時 vs 実行時ポリモーフィズムインターフェース vs 抽象クラス複数のインターフェース明示的なインターフェースアップキャストとダウンキャストまとめ - 図形計算機