ラベル virtual の投稿を表示しています。 すべての投稿を表示
ラベル virtual の投稿を表示しています。 すべての投稿を表示

2018年2月20日火曜日

オーバーライド、仮想メソッド

今回は、継承の中で、同じ名前で機能を書き換える、オーバーライドのお話です。

オーバーライド


前回のエントリーで、既存のクラスに機能追加を行って、新たなクラスを作る継承について書きました。しかし、継承にはこれだけではなく、派生クラスに、基底クラスと同じ名前で異なったメソッドを持たせる事が出来る機能があります。これをオーバーライドと言います。基底クラスのメソッドを継承クラスで上書きしてしまうものです。オーバーライドするメソッドは、overrideというキーワードを付けて宣言します。

仮想メソッド


オーバーライドされる側のメソッドを仮想メソッドと呼んでいます。メソッドの定義でvirtualというキーワードを付けて宣言します。

下に、学習したときに入力したプログラムリストを示します。

using System;

namespace sample7
{
    class Program
    {
        static void Main(string[] args)
        {
            EnhancedMyObject obj = new EnhancedMyObject();
            obj.name = "Gustav";
            obj.age = 56;
            obj.mail = "mail@truth-maker.com";
            obj.printData();
            Console.ReadKey();
        }
    }

    class MyObject
    {
        public string name = "(noname)";
        public int age = 0;
        public virtual void printData()
        {
            Console.WriteLine("名前 : {0}, 年齢 : {1}歳",name, age);
        }

    }

    class EnhancedMyObject : MyObject
    {
        public string mail = "(no mail)";

        public override void printData()
        {
            Console.WriteLine("名前 : {0}, 年齢 : {1}歳, メール : {2}.", name, age, mail);
        }
    }
}

MyObjectクラスの中で、キーワードvirtual付きで定義されているメソッドが仮想メソッドです。仮想という名前は付いていますが、オーバーライド対象がなければ、このメソッドが実行されます。

EnhancedMyObjectで定義されている、キーワードoverride付きで定義されているメソッドがオーバーライドメソッドです。EnhancedMyObjectクラスはMyObjectクラスの派生クラスですが、printData()メソッドはEnhancedMyObjectクラスで定義されているprintData()メソッドに上書きされてしまいます。

newによるオーバーライド


継承によるオーバーライド、キーワードoverrideによるオーバーライドの他に、キーワードnewによるオーバーライドというものがあります。

using System;

namespace sample8
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObject obj = new EnhancedMyObject();
            obj.name = "Gustav";
            obj.age = 56;
            obj.printData();

            EnhancedMyObject obj2 = (EnhancedMyObject)obj;
            obj2.mail = "mail@truth-maker.com";
            obj2.printData();
            Console.ReadKey();
        }
    }

    class MyObject
    {
        public string name = "(noname)";
        public int age = 0;
        public virtual void printData()
        {
            Console.WriteLine("名前 : {0}, 年齢 : {1}歳",name, age);
        }
    }

    class EnhancedMyObject : MyObject
    {
        public string mail = "(no mail)";
        public new void printData()
        {
            Console.WriteLine("名前 : {0}\n\t 年齢 : {1}歳\n\t メール : {2}.", name, age, mail);
        }
    }
}

上のリストにおいて、基底クラスのメソッドprintData()を派生クラスのprintData()でオーバーライドするのですが、

public new void printData()

という宣言になっています。これを実行すると

MacBook-Pro:sample8 gustav$ mcs Program.cs
MacBook-Pro:sample8 gustav$ mono Program.exe
名前 : Gustav, 年齢 : 56歳
名前 : Gustav
         年齢 : 56歳
         メール : mail@truth-maker.com


となります。

まず、

            MyObject obj = new EnhancedMyObject();

において、MyObject型の変数objを宣言して、EnhancedMyObject型のオブジェクトのインスタンスを生成、その参照をobjに代入します。そのオブジェクトのprintData()メソッドで出力したのが、最初の出力行です。これは、基底クラスで定義しているメソッドの動作です。

次に、

            EnhancedMyObject obj2 = (EnhancedMyObject)obj;

で、EnhancedMyObject型の変数obj2を宣言し、最初にnewで生成したオブジェクトへの参照を保持している変数objをEnhancedMyObject型にキャストしてobj2に代入しています。これは、型の変換はあるものの、同じオブジェクト(最初に生成したEnhancedMyObject型のオブジェクト)への参照です。

しかし、変数obj2が示すオブジェクトのprintData()メソッドで出力したものは、最初の出力と異なり、派生クラスでnew付きで定義したメソッドの動作です。

同じオブジェクトへの参照ですが、変数の型によって動くメソッドが変わっているのです。もしも派生クラスのメソッドがnewではなくてoverrideで定義されていると、出力結果は両方とも同じ、派生クラスで定義されたメソッドの動作になります。

このように、overrideでは、オーバーライドされるメソッドが完全に書き換えられてしまいますが、newでは基底クラスのメソッドも失われることなく残り、同じインスタンスであっても、参照のしかたで使われるメソッドを使い分けることができます。

今回はここまでです。