C# の継承 - base キーワードで親クラスのコンストラクタを呼び出す

クラスを継承したとき、子クラスのインスタンスが生成される前に親クラスのコンストラクタが実行されます。親クラスに引数付きのコンストラクタしか存在しない場合、子クラスから明示的に呼び出す必要があり、そこで使うのが base キーワードです。

引数なしコンストラクタの場合

親クラスが引数なしのコンストラクタだけを持っている場合、子クラス側で特別な記述は不要です。C# が自動的に親クラスのコンストラクタを呼び出してくれます。

class Animal
{
    public Animal()
    {
        Console.WriteLine("Animal のコンストラクタ");
    }
}

class Dog : Animal
{
    public Dog()
    {
        Console.WriteLine("Dog のコンストラクタ");
    }
}

この Dog クラスをインスタンス化すると、まず Animal のコンストラクタが実行され、そのあとに Dog のコンストラクタが実行されます。出力は「Animal のコンストラクタ」→「Dog のコンストラクタ」の順です。

base で引数を渡す

親クラスが引数付きのコンストラクタのみを定義している場合、子クラスのコンストラクタから base(...) を使って明示的に値を渡す必要があります。

class Animal
{
    public string Name { get; }

    public Animal(string name)
    {
        Name = name;
    }
}

class Dog : Animal
{
    public string Breed { get; }

    public Dog(string name, string breed) : base(name)
    {
        Breed = breed;
    }
}

: base(name) の部分が親クラスのコンストラクタ呼び出しにあたります。Dog のコンストラクタが受け取った name を、そのまま Animal(string name) に転送しているわけです。

この : base(name) を省略すると、コンパイラは引数なしの Animal() を探しに行きますが、定義されていないためコンパイルエラーになります。エラーメッセージは CS7036 として表示されます。

「‘Animal’ には引数 0 個のコンストラクタがありません」という内容のエラー。

base を書かないとどうなるか

: base(...) を省略した場合の動作は、親クラスの状態によって変わります。

引数なしコンストラクタがある場合

暗黙的に base() が呼ばれるため、エラーにはならない。明示的に書いても書かなくても同じ動作になる。

引数なしコンストラクタがない場合

コンパイラが呼び出すべきコンストラクタを見つけられず、コンパイルエラーが発生する。

つまり、親クラスにデフォルトコンストラクタが存在するかどうかがポイントです。引数付きコンストラクタを 1 つでも定義すると、C# はデフォルトコンストラクタを自動生成しなくなるため、子クラス側で base を使った明示的な呼び出しが必須になります。

複数のコンストラクタを持つ親クラス

親クラスが複数のコンストラクタをオーバーロードしている場合、base に渡す引数の型と数によってどのコンストラクタが呼ばれるか決まります。

class Animal
{
    public string Name { get; }
    public int Age { get; }

    public Animal(string name)
    {
        Name = name;
        Age = 0;
    }

    public Animal(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

class Cat : Animal
{
    public Cat(string name) : base(name) { }

    public Cat(string name, int age) : base(name, age) { }
}

Cat(string name)Animal(string name) を呼び出し、Cat(string name, int age)Animal(string name, int age) を呼び出します。引数のシグネチャが一致するコンストラクタが選択される仕組みです。

base でメソッドを呼び出す

base はコンストラクタだけでなく、親クラスのメソッドやプロパティにアクセスする際にも使えます。特にオーバーライドしたメソッドの中で、親クラス側の元の処理を呼び出したいときに便利です。

class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("...");
    }
}

class Dog : Animal
{
    public override void Speak()
    {
        base.Speak();
        Console.WriteLine("ワン!");
    }
}

base.Speak() によって親クラスの Speak メソッドを実行したうえで、子クラス独自の処理を追加しています。親の振る舞いを完全に置き換えるのではなく、拡張したいときに有効な書き方です。