継承

スポンサーリンク

継承

次のプログラムは,時計を表現する Clock クラスと,チャイム機能付きの時計を表現する ChimeClock クラスを定義したものです。
ChimeClock クラスは Clock クラスを基にして作られています。

using System;

// 時計を表す
class Clock
{
    public int Hour;    // 時
    public int Minute;  // 分

    // 時刻を表示する
    public virtual void PrintTime()
    {
        Console.WriteLine("{0:00}:{1:00}", Hour, Minute);
    }
}

// チャイム付きの時計を表す
class ChimeClock : Clock  // Clock を継承
{
    public override void PrintTime()  // オーバーライド
    {
        base.PrintTime();  // 基底クラスの PrintTime を呼び出し
        if (Minute == 0) { Console.WriteLine("チャイム"); }
    }
}

class Program
{
    static void Main()
    {
        ChimeClock clock = new ChimeClock();
        clock.Hour = 15;
        clock.Minute = 0;
        clock.PrintTime();
    }
}
15:00
チャイム

あるクラスを基にして新しいクラスを作ることを,クラスの継承 (inheritance) といいます。
今回の例では,ChimeClock クラスが Clock クラスを継承しています。
また,Clock クラスを基底クラス (base class),ChimeClock クラスを派生クラス (derived class) と呼びます。

クラスを継承するには,派生クラスの定義を次のように書きます。

class クラス名 : 基底クラス名 { ... }

オーバーライド

基底クラスで定義済のメンバを派生クラス側で定義し直すことを,オーバーライド (override) といいます。

あるメンバをオーバーライドするには,派生クラス側の定義に override 修飾子を付けます。
オーバーライドされるメンバにも,virtual, override, abstract 修飾子のいずれかが必要です。

オーバーライドされている基底クラス側のメンバにアクセスするには,base.メンバ名 と書きます。

基底メンバの隠蔽

override 修飾子の代わりに new 修飾子を使うと,基底クラス側のメンバは隠蔽されます。
メンバの隠蔽では,オーバーライドのように返却型を一致させる必要はありません。

class ChimeClock : Clock
{
    public new bool PrintTime()  // 基底クラスの PrintTime を隠蔽
    {
        ...
    }
}

多態性

基底クラスの変数には,派生クラスのインスタンスも入れることができます。

class Program
{
    static void Main()
    {
        Clock clock = new ChimeClock();
        clock.Hour = 15;
        clock.Minute = 0;
        clock.PrintTime();
    }
}
15:00
チャイム

このとき呼び出されるメソッドは,変数の型に関係なく,インスタンスの型に応じて決まります。
この性質をポリモーフィズム (polymorphism) または多態性といいます。

コンストラクタ

派生クラスから基底クラスのコンストラクタを呼び出すには,次のように : base(...) と書きます。

class CDerived : CBase
{
    public CDerived(int a)
        : base(a)    // 基底クラスのコンストラクタを呼び出す
    {
        ...
    }
}

ボックス化

C# のすべての型は object 型を暗黙のうちに継承しています。
そのため,任意の値型は object 型の変数に収め,オブジェクトとして扱うことができます。
この操作をボックス化といい,逆の操作をボックス化解除といいます。

int a = 123;
object o = a;     // ボックス化
int b = (int) o;  // ボックス化解除

sealed 修飾子

クラスの定義に sealed 修飾子を付けると,当該クラスの継承を禁止できます。

sealed class SealedClass { ... }

同様に,メソッド,プロパティの定義に sealed 修飾子を付けると,派生クラスでのオーバーライドを禁止できます。

抽象クラス

処理部分が未定義なメソッドやプロパティをメンバに持つクラスを定義できます。
こうしたクラスを,抽象クラス (abstract class) といいます。

abstract class ClockBase  // 抽象クラス
{
    public int Hour;
    public int Minute;

    public abstract void PrintTime();  // 抽象メソッド
}

class Clock : ClockBase
{
    public override void PrintTime()
    {
        Console.WriteLine("{0:00}:{1:00}", Hour, Minute);
    }
}

抽象クラスは基底クラス専用のクラスであり,インスタンス化はできません。

インタフェース

抽象メソッド,抽象プロパティだけしか持たない抽象クラスは,インタフェース (interface) として定義できます。

interface IClock
{
    int Hour { get; set; }
    int Minute { get; set; }

    void PrintTime();
}

class Clock : IClock  // IClock を実装
{
    private int hour;
    private int minute;

    public int Hour
        { get { return hour; } set { hour = value; } }
    public int Minute
        { get { return minute; } set { minute = value; } }

    public void PrintTime()
    {
        Console.WriteLine("{0:00}:{1:00}", Hour, Minute);
    }
}

インタフェースを基にしてクラスを定義することを,インタフェースの実装 (implementation) といいます。
この例では,Clock クラスが IClock インタフェースを実装しています。

インタフェースのメンバはデフォルトで public かつ abstract なので,いずれの修飾子も付ける必要はありません。

C# では,派生クラスが複数の基底クラスを持つこと (多重継承) は許されていません。
ただし,クラスが複数のインタフェースを実装することは許されています。

スポンサーリンク