継承 (Inheritance)

class Base
{
}

class Sub : Base
{
}

継承方法の指示

  • virtual … 派生クラスに、オーバーライドを許可
  • abstract … 派生クラスに、オーバーライドを強制
抽象メソッドと仮想メソッド - 継承 (C# プログラミング ガイド) | Microsoft Learn

virtual … オーバーライドを許可

class Base
{
    public         void Method1() { }
    public virtual void Method2() { }
    public virtual void Method3() { }
}

class Sub : Base
{
    public          void Method1() { } // CS0108 'Sub.Method1()' は継承されたメンバー 'Base.Method1()' を非表示にします。非表示にする場合は、キーワード new を使用してください。
    public          void Method2() { } // CS0114 'Sub.Method2()' は継承されたメンバー 'Base.Method2()' を非表示にします。現在のメンバーでその実装をオーバーライドするには、override キーワードを追加してください。オーバーライドしない場合は、new キーワードを追加してください。
    public override void Method3() { } // OK
}

abstract … オーバーライドを強制

class Base1
{
    public abstract void Method1(); // CS0513 'Base1.Method1()' は抽象ですが、非抽象クラスの 'Base1' に含まれています。
}

abstract class Base2
{
    public abstract void Method1() { } // CS0500 'Base2.Method1()' は abstract に指定されているため本体を宣言できません。
    public abstract void Method2();    // OK
}

class Sub : Base2 // CS0534 'Sub' は継承抽象メンバー 'Base2.Method1()' を実装しません。
{
    public          void Method1() { } // CS0114 'Sub.Method1()' は継承されたメンバー 'Base2.Method1()' を非表示にします。現在のメンバーでその実装をオーバーライドするには、override キーワードを追加してください。オーバーライドしない場合は、new キーワードを追加してください。
    public override void Method2() { } // OK
}

継承の実装

override … メンバの拡張

virtualやabstractで宣言されているメンバを、拡張して実装します。

abstract class Base1
{
    public          void Method1() { }
    public virtual  void Method2() { }
    public abstract void Method3();
}

class Sub : Base1
{
    public override void Method0() { } // CS0115 'Sub.Method0()': オーバーライドする適切なメソッドが見つかりませんでした。
    public override void Method1() { } // CS0506 'Sub.Method1()': 継承されたメンバー 'Base1.Method1()' は virtual、abstract または override に設定されていないためオーバーライドできません。
    public override void Method2() { } // OK
    public override void Method3() { } // OK
}

base

派生クラス (derived class) から、オーバーライドした基本クラス (base class / 基底クラス) のメンバを呼び出せます。base キーワード - C# リファレンス | Microsoft Learn

public override void Method2()
{
    base.Method2();
}

new … メンバの隠蔽

class Base1
{
    protected virtual void Method1() { }
    protected virtual void Method2() { }
    protected virtual void Method3() { }
    protected virtual void Method4() { }
}

class Sub : Base1
{
    public    override void Method1() { } // CS0507 'Sub.Method1()': 'protected' の継承メンバー 'Base1.Method1()' をオーバーライドするときに、アクセス修飾子を変更できません。
    public    new      void Method2() { } // OK
    protected override void Method3() { } // OK
    protected new      void Method4() { } // OK
}

アクセス修飾子の変更

派生クラスでnewで非表示にしたメンバの、アクセス修飾子を変更する場合を考えます。

class Base1
{
    public  void Method1() { Console.Write("Base"); }
    public  void Method2() { Console.Write("Base"); }
    private void Method3() { Console.Write("Base"); }
    private void Method4() { Console.Write("Base"); }
}

class Sub : Base1
{
    public  new void Method1() { Console.Write("Sub"); }
    private new void Method2() { Console.Write("Sub"); }
    public  new void Method3() { Console.Write("Sub"); } // warning CS0109: メンバー 'Sub.Method3()' は継承されたメンバーを非表示にしません。new キーワードは不要です。
    private new void Method4() { Console.Write("Sub"); } // warning CS0109: メンバー 'Sub.Method4()' は継承されたメンバーを非表示にしません。new キーワードは不要です。

    public void Func()
    {
        Method1(); // Sub
        Method2(); // Sub … 派生クラスのメンバへアクセス可能なため、それが呼ばれる
        Method3(); // Sub
        Method4(); // Sub
    }
}

派生クラスのインスタンスから呼ぶ場合は、アクセス可能なメンバが呼ばれます。

Sub sub = new Sub();
sub.Method1(); // Sub
sub.Method2(); // Base … 派生クラスのメンバはprivateなため、基本クラスのそれが呼ばれる
sub.Method3(); // Sub
sub.Method4(); // error CS0122: 'Sub.Method4()' はアクセスできない保護レベルになっています

overrideとnewの相違

基本クラスと、それを継承した派生クラスを定義して検証します。

class Base
{
    public virtual void Method1() { Console.Write("Base"); }
    public virtual void Method2() { Console.Write("Base"); }
}

class Sub : Base
{
    public override void Method1() { Console.Write("Sub"); }
    public new      void Method2() { Console.Write("Sub"); }
}

次の3通りの場合について、それぞれ呼ばれるメソッドを確認します。

  1. 基本クラスに、基本クラスのインスタンスを格納
  2. 派生クラスに、派生クラスのインスタンスを格納
  3. 基本クラスに、派生クラスのインスタンスを格納
// 1
Base c1 = new Base();
c1.Method1(); // Base … 基本クラスのメソッドが呼ばれる
c1.Method2(); // Base … 同上

// 2
Sub c2 = new Sub();
c2.Method1(); // Sub … 派生クラスのメソッドが呼ばれる
c2.Method2(); // Sub … 同上

// 3
Base c3 = new Sub();
c3.Method1(); // Sub … overrideで拡張されていると、派生クラスのメソッドが呼ばれる
c3.Method2(); // Base … newで隠蔽されていると、基本クラスのメソッドが呼ばれる

継承したクラスをさらに継承した場合

class C1
{
    public virtual void M1() { Console.Write("C1"); }
    public virtual void M2() { Console.Write("C1"); }
    public virtual void M3() { Console.Write("C1"); }
}

class C2 : C1
{
    public override void M1() { Console.Write("C2"); }
    public override void M2() { Console.Write("C2"); }
    public new void M3() { Console.Write("C2"); }
}

class C3 : C2
{
    public override void M1() { Console.Write("C3"); }
    public new void M2() { Console.Write("C3"); }
    public new void M3() { Console.Write("C3"); }
}
C3 c3 = new C3();

((C1)c3).M1(); // C3
((C1)c3).M2(); // C2
((C1)c3).M3(); // C1 … C2.M3()がnewで隠蔽されているため

((C2)c3).M1(); // C3
((C2)c3).M2(); // C2 … C3.M2()がnewで隠蔽されているため
((C2)c3).M3(); // C2 … C3.M3()がnewで隠蔽されているため

c3.M1(); // C3
c3.M2(); // C3
c3.M3(); // C3

newで隠蔽されていると、呼び出し元の型のメソッドが呼ばれます。

Microsoft Learnから検索