C#では、_継承_を使って_クラス_の階層を定義できます。継承を使うと、派生クラス(Car)が親クラス(Vehicle)の振る舞いとデータを引き継げます。親クラスを指定しない場合、そのクラスはobjectクラスを継承します。
親クラスは、次の3つの方法で派生クラスに機能を提供できます。
virtualメソッドを定義します。これは通常のメソッドに似ていますが、派生クラスが変更_できる_メソッドです。abstractメソッドを定義します。これは実装を持たないメソッドで、派生クラスが必ず実装しなければならないものです。abstractメソッドを持つクラスは、そのクラス自体もabstractとしてマークする必要があります。抽象クラスはインスタンス化できません。protectedアクセス修飾子を使うと、親クラスのメンバーに派生クラスからアクセスできますが、他のクラスからはアクセスできません。
派生クラスは、baseキーワードを通じて親クラスのメンバーにアクセスできます。
// Inherits from the 'object' class
abstract class Vehicle
{
// Can be overridden
public virtual void Drive()
{
}
// Must be overridden
protected abstract int Speed();
}
class Car : Vehicle
{
public override void Drive()
{
// Override virtual method
// Call parent implementation
base.Drive();
}
protected override int Speed()
{
// Implement abstract method
}
}
派生クラスのコンストラクターは、自身のコンストラクターの処理を実行する_前に_、親クラスのコンストラクターを自動的に呼び出します。親クラスのコンストラクターには、baseキーワードを使って引数を渡せます。
abstract class Vehicle
{
protected Vehicle(int wheels)
{
Console.WriteLine("Called first");
}
}
class Car : Vehicle
{
public Car() : base(4)
{
Console.WriteLine("Called second");
}
}
複数のクラスが1つの基底クラスから派生している場合、それら2つ(またはそれ以上)のクラスは、基底クラスのメソッドに対してしばしば異なるバージョンを実装します。これはポリモーフィズムと呼ばれる、とても重要な原則です。たとえば、上の例を少し変えた次の例では、Vehicleを使うコードが、どの種類の乗り物がインスタンス化されたかに応じて振る舞いを変えられることを示しています。
abstract class Vehicle
{
public abstract string GetDescription();
}
class Car : Vehicle
{
public Car()
{
}
public override string GetDescription()
{
return "Runabout";
}
}
class Rig : Vehicle
{
public Rig()
{
}
public override string GetDescription()
{
return "Big Rig";
}
}
Vehicle v1 = new Car();
Vehicle v2 = new Rig();
v1.GetDescription();
// => Runabout
v2.GetDescription();
// => Big Rig
この演習では、「Wizard and Warriors」という名前のロールプレイングゲームで遊びます。WizardかWarriorのどちらかとしてプレイできます。
WarriorとWizardでは、与えるダメージポイントを決めるルールが異なります。
Warriorのルールは次のとおりです。
Wizardのルールは次のとおりです。
通常、キャラクターが脆弱になることはありません。ただし、Wizardは呪文を準備していない場合、脆弱になります。
WarriorとWizardのキャラクターを扱う6つのタスクがあります。
CharacterクラスのToString()メソッドをオーバーライドして、キャラクターの説明を"Character is a <CHARACTER_TYPE>"という形式で返すようにします。
var warrior = new Warrior();
warrior.ToString();
// => "Character is a Warrior"
Character.Vulnerable()メソッドが常にfalseを返すようにします。
var warrior = new Warrior();
warrior.Vulnerable();
// => false
Wizard.PrepareSpell()メソッドを実装して、Wizardが事前に呪文を準備できるようにします。
var wizard = new Wizard();
wizard.PrepareSpell();
Wizardが呪文を準備していない場合は、Vulnerable()メソッドがtrueを返すようにします。それ以外の場合はfalseを返します。
var wizard = new Wizard();
wizard.Vulnerable();
// => true
Wizard.DamagePoints()メソッドを実装して、与えるダメージポイントを返すようにします。呪文を準備している場合は12ダメージポイント、準備していない場合は3ダメージポイントです。
var wizard = new Wizard();
var warrior = new Warrior();
wizard.PrepareSpell();
wizard.DamagePoints(warrior);
// => 12
Warrior.DamagePoints()メソッドを実装して、与えるダメージポイントを返すようにします。対象が脆弱な場合は10ダメージポイント、脆弱でない場合は6ダメージポイントです。
var warrior = new Warrior();
var wizard = new Wizard();
warrior.DamagePoints(wizard);
// => 10