継承 どちらが実行されるのか

基本用法

基底クラスと継承クラス(派生クラス)との関係

基底クラスに定義されたメンバを継承クラスで上書きするには以下の2通りある

1.基底クラス側でvirtual指定し継承クラス側でoverride指定する
2.基底クラス側の通常のメソッド等に対しnew指定して同名で定義する

実際に基底クラスのメソッドか継承クラスのメソッドかのどちらが実行されるかは以下のサンプルコードのとおり
    // 基底クラス 
    class BaseClass
    {
        // virtual 指定あり
        public virtual void Method_A() => Console.WriteLine("BaseClass Method_A");

        // virtual 指定なし
        public void Method_B() => Console.WriteLine("BaseClass Method_B");
    }

    // 継承クラス
    class ExtendedClass : BaseClass
    {
        // virtual メソッドを override
        public override void Method_A() => Console.WriteLine("ExtendedClass Method_A");

        // 通常のメソッドを new で上書き
        new public void Method_B() => Console.WriteLine("ExtendedClass Method_B");
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            BaseClass base1 = new BaseClass();
            base1.Method_A();      //  BaseClass 
            base1.Method_B();      //  BaseClass 

            ExtendedClass extend1 = new ExtendedClass();
            extend1.Method_A();    //  ExtendedClass 
            extend1.Method_B();    //  ExtendedClass 

            // 実行時エラー
            //((ExtendedClass)base1).Method_A(); //InvalidCastException
            //((ExtendedClass)base1).Method_B(); //InvalidCastException

            // 継承クラスを基底クラスにキャストして実行
            ((BaseClass)extend1).Method_A();  // ExtendedClass 
            ((BaseClass)extend1).Method_B();  // BaseClass

            // 基底クラス型の宣言に継承クラスのインスタンスを代入
            BaseClass base2 = new ExtendedClass();
            base2.Method_A();  // ExtendedClass 
            base2.Method_B();  // BaseClass

            // 参考
            var t = base2.GetType(); // ExtendedClass

            //ExtendedClass extend2 = new BaseClass(); コンパイル不可(CS0266:暗黙的変換エラー)

        }
    }

複数のインタフェースに同一のメソッド

C#言語は多重継承はできないが、インタフェースはいくつでも実現(継承)可能である
異なるインタフェースに同一のシグネチャが定義されている場合は、以下のサンプルのようになる
interface ISampleA
{
    string GetString();
}
 
interface ISampleB
{
    string GetString();
}
 
public class Sample : ISampleA,ISampleB
{
 
    public string GetString() => "Sample";
 
    string ISampleA.GetString() => "ISampleA";
 
    string ISampleB.GetString() => "ISampleB";
}

class Program
{
    static void Main(string[] args)
    {
        Sample sample = new Sample();
 
        string ret1 = sample.GetString(); // Sample
         
        string ret2 = ((ISampleA)sample).GetString(); // ISampleA
 
        string ret3 = ((ISampleB)sample).GetString(); // ISampleB
    }
}

デフォルト実装をもつインタフェースとの関係

C#8.0以降でインタフェースに基底処理(デフォルト実装)が可能になりました

    interface ISampleA
    {
        void MethodA();

        void MethodB() => Console.WriteLine("ISampleA.MethodB");
    }

    interface ISampleB
    {
        void MethodA();

        void MethodB() => Console.WriteLine("ISampleB.MethodB");
    }

    // 実現(継承)クラスはインタフェースのデフォルト実装にかかわらない
    public class Sample : ISampleA, ISampleB
    {
        public void MethodA() => Console.WriteLine("Sample.MethodA"); 
    }

    class Program
    {
        static void Main(string[] args)
        {
            var sample = new Sample();

            sample.MethodA();   // Sample.MethodA

            // 実体クラス側はデフォルト実装を知らない
            // sample.MethodB();

            // デフォルト実装側を実行するにはキャストが必要

            ((ISampleA)sample).MethodA();   // Sample.MethodA
            ((ISampleA)sample).MethodB();   // ISampleA.MethodB

            ((ISampleB)sample).MethodA();   // Sample.MethodA
            ((ISampleB)sample).MethodB();   // ISampleB.MethodB

        }
    }