C# virtual,overideの意味がわかる例。


virtual,overideの意味がわかる例です。
今回はコンソール。

Baseというのが基本クラス。
Inheritが継承するクラス。
それぞれメソッドでWriteLineしてどのメソッドが呼ばれたかを表示します。

using System;

namespace OverrideTest
{
    class Base
    {
        public virtual void Test1()
        {
            Console.WriteLine("BaseのTest1が呼ばれました");
        }
        public void Test2()
        {
            Console.WriteLine("BaseのTest2が呼ばれました");
        }
    }

    class Inherit : Base
    {
        public override void Test1()
        {
            Console.WriteLine("InheritのTest1が呼ばれました");
        }

        public new void Test2()
        {
            Console.WriteLine("InheritのTest2が呼ばれました");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Base Base = new Base();
            Base.Test1();//BaseのTest1が呼ばれました
            Base.Test2();//BaseのTest2が呼ばれました
            Console.WriteLine();//見やすいように改行

            Inherit Inherit = new Inherit();
            Inherit.Test1();//InheritのTest1が呼ばれました
            Inherit.Test2();//InheritのTest2が呼ばれました
            Console.WriteLine();//見やすいように改行

            Base Test = new Inherit();//継承元のクラスには、暗黙的に型変換できる
            Test.Test1();//InheritのTest1が呼ばれました
            Test.Test2();//BaseのTest2が呼ばれました

            Console.WriteLine("Press any key to continue");
            Console.ReadKey();
        }
    }
}

動作結果は以下のようなものになります。

さて、少し詳しくmain処理を見ていきます。

            Base Base = new Base();
            Base.Test1();//BaseのTest1が呼ばれました
            Base.Test2();//BaseのTest2が呼ばれました
            Console.WriteLine();//見やすいように改行

            Inherit Inherit = new Inherit();
            Inherit.Test1();//InheritのTest1が呼ばれました
            Inherit.Test2();//InheritのTest2が呼ばれました
            Console.WriteLine();//見やすいように改行

ここはわかりやすいと思います。
それぞれの型のTest1およびTest2メソッドが呼ばれます。

            Base Test = new Inherit();//継承元のクラスには、暗黙的に型変換できる
            Test.Test1();//InheritのTest1が呼ばれました
            Test.Test2();//BaseのTest2が呼ばれました

問題は最後のここです。
継承すると継承元のクラスには暗黙的に型変換できます。
ここでは、Inherit型でnewして、Base型の変数(Test)に暗黙的に型変換して代入しています。
さて、TestからTest1及びTest2を呼び出すとどうなるのか。

overrideで記述している場合(Test1)は、newした時の型(Inherit)のメソッドが呼ばれます。
そうでない場合(Test2)は、保存されている型(Base)のメソッドが呼ばれます。

"override"日本語で言うと、上書きです。
Baseで定義したTest1を、Inheritクラスで上書きしていることになります。

実は、このオーバーライドの動きを理解すると、オブジェクト指向の"多態性"という考えが理解できます。

例えば行列演算です。ソフトウェアはDeepLearningを行うソフトで、Test1は逆行列を計算するメソッドだったとします。

このとき、2×2くらいの小さい行列の演算を何度も行う場合と、100×100くらいの巨大な行列の演算を行う場合では、最適なアルゴリズムは違ってきます。GPUを使うかCPUを使うかでも最適なアルゴリズムは変わってくるでしょう。

例えばBaseでは小さい行列の演算に最適化されたアルゴリズムになっていて、Inheritが大きい行列に特化したアルゴリズムだとすると、オーバーライドをするだけで、DeepLearning全体の処理には変更を加えることなく、処理速度を上げることができます。