ポリモーフィズム

目次

ポリモフィズム

ポリモフィズムとは、宣言した変数の型ではなく、実体であるインスタンスに応じて呼び出されるメソッドが決まること。これにより、1つのメソッドで、クラスに応じた適切な処理が行える。

理解編

オーバーライド

オーバーライドとは、隠蔽と同じように基本クラスのメソッドを派生クラスで書き換えできる機能である。オーバーライドするには、基本クラスのメソッドにvertial、派生クラスのメソッドにoverrideをそれぞれ付けてメソッドを宣言すればよい。基本クラスでvertialが宣言されていないメソッドは、オーバーライドできない。

継承関係のあるクラスでは、派生クラスから基本クラスへの変換、または基本クラスから派生クラスへの変換が可能である。派生クラスから基本クラスへの変換をアップキャスト、基本クラスから派生クラスへの変換をダウンキャストと呼ぶ。

隠蔽との違い

隠蔽との違いを次にまとめる。 - 隠蔽は new を宣言するだけで利用できる。一方、オーバーライドはvirtualoverrideをセットで宣言する必要がある。 - 隠蔽では、基本メソッドと派生メソッドとで アクセス修飾子戻り値 を一致させる必要はない。 - 隠蔽は想定外のメソッドを書き換えてしまう可能性がある、 - 隠蔽の場合、アップキャストすると派生クラスで再定義した機能が使用できない。

上記の理由により、ポリモフィズムを実現したい場合、隠蔽を使用せずにオーバーライドでメソッドを再定義すべきらしい。

実践編

オーバーライド

オバーライドを使用したコードを次に示す。

    // 基本クラス
    class Music
    {
        public virtual void BaseInfo()
        {
            Console.WriteLine("Music");
        }
    }

    // 派生クラス、オーバーライドあり
    class Pop : Music
    {
        public override void BaseInfo()
        {
            Console.WriteLine("Pop");
        }

    }
    // 派生クラス、隠ぺいあり
    class Jazz : Music
    {
        new public void BaseInfo()
        {
            Console.WriteLine("Jazz");
        }

    }


    class MainClass
    {
        static void Main(string[] args)
        {
            // 基本クラスMusicのインスタンス生成
            Music music = new Music();
            music.BaseInfo();

            // 派生クラスPopのインスタンス生成
            Pop pop = new Pop();
            pop.BaseInfo();

            // 派生クラスJazzのインスタンス生成
            Jazz jazz = new Jazz();
            jazz.BaseInfo();
        }
    }

結果を次に示す。

Music
Pop
Jazz

隠蔽との違い

    // 基本クラス
    class Music
    {
        public virtual void BaseInfo()
        {
            Console.WriteLine("Music");
        }
    }


    // 派生クラス
    class Pop : Music
    {
        // オーバーライド
        public override void BaseInfo()
        {
            Console.WriteLine("Pop");
        }
        public void PopInfo()
        {
            Console.WriteLine("PopInfo");
        }
    }

    // 派生クラス
    class Jazz : Music
    {
        // 隠蔽
        new public void BaseInfo()
        {
            Console.WriteLine("Jazz");
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            // アップキャスト
            // PopクラスのインスタンスをMusicクラスの変数に代入
            Music music = new Pop();
            music.BaseInfo();

            // ダウンキャスト:成功例
            // Musicクラスの変数をPopクラスの変数に代入
            Pop pop = (Pop)music;
            pop.BaseInfo();
            pop.PopInfo();

            // ダウンキャスト:失敗例
            // Musicクラスの変数をJazzクラスの変数に代入
            // 元クラスがPopクラス(継承関係が異なるクラス)であるためエラーとなる。
            // Jazz jazz = (Jazz)music;
        }
    }

結果を次に示す。

Pop
Pop
PopInfo

インタフェース

理解編

インタフェース

インタフェースとは、メソッドの呼び出しだけを定義したものである。メソッドの処理内容は継承したクラスで定義することになる。継承クラスにメソッドの処理内容を記載することを 実装する という。インタフェースは次のように定義すればよい。インタフェースとクラスを区別するために、先頭にIを付けるとよいらしい。

interface インタフェース名
{
    //メンバーの宣言
}

また、インタフェースはプロパティをもつこともできる。

インタフェースの多重継承と多重実装

インタフェースは継承が可能である。インタフェースを継承する場合は、クラスの継承と同じようにすればよい。また、インタフェースはクラスの継承とは異なり、多重継承が可能である。多重継承する場合は、基本インタフェースを , で区切ればよい。

interface 継承インタフェース : 基本インタフェース
{
  メンバー宣言
}

また、インタフェースは1つのクラスに複数のインタフェースを実装できる。

実践編

インタフェース

インタフェースを使用したコードを次に示す。

    interface IGetInfo
    {
        void Getinfo();
    }

    class Sample : IGetInfo
    {
        public void Getinfo()
        {
            Console.WriteLine("test");
        }
    }

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

            inst.Getinfo();

            Console.ReadKey();
        }
    }

結果を次に示す。

test

プロパティを用いたインタフェースのコードを次に示す。

    interface Ipoint
    {
        int Px { get; set; }
        int Py { get; set; }
    }

    class ReversePoint : Ipoint
    {
        int x;
        int y;

        // コンストラクタ
        public ReversePoint(int xdata, int ydata)
        {
            this.x = xdata;
            this.y = ydata;
        }

        // Property
        public int Px
        {
            get { return -x; }
            set { x = value; }
        }

        public int Py
        {
            get { return -y; }
            set { y = value; }
        }
    }


    class MainClass
    {
        public static void DisplayPoint(Ipoint point)
        {
            Console.WriteLine("x={0}, y={1}", point.Px, point.Py);
        }

        static void Main(string[] args)
        {

            int VAL_PX = -10;
            int VAL_PY = 100;

            ReversePoint p1 = new ReversePoint(VAL_PX, VAL_PY);
            Console.WriteLine($"PX is {VAL_PX}, PY is {VAL_PY}");
            Console.WriteLine($"invert PX is {p1.Px}, invert PY is {p1.Py}");

            VAL_PX = 100;
            VAL_PY = 1;

            p1.Px = VAL_PX;
            p1.Py = VAL_PY;
            Console.WriteLine($"PX is {VAL_PX}, PY is {VAL_PY}");
            Console.WriteLine($"invert PX is {p1.Px}, invert PY is {p1.Py}");

            Console.ReadKey();
        }
    }
}

結果を次に示す。

PX is -10, PY is 100
invert PX is 10, invert PY is -100
PX is 100, PY is 1
invert PX is -100, invert PY is -1
インタフェースの多重継承と多重実装

インタフェースの多重継承と多重実装を使用したコードを次に示す。

    interface IBase01Interface
    {
        void Method01(int a);
    }
    interface IBase02Interface
    {
        void Method02(int a);
    }

    // インタフェースの多重継承
    interface IBase03Interface : IBase01Interface, IBase02Interface
    {
        void Method03(int a);
    }

    // インタフェースの多重実装

    class Sample : IBase01Interface, IBase02Interface
    {
        public void Method01(int a)
        {
            Console.WriteLine($"Sample a is {a}");
        }
        public void Method02(int a)
        {
            Console.WriteLine($"Sample a is {a * a}");
        }
    }

    class Test : IBase03Interface
    {
        public void Method01(int a)
        {
            Console.WriteLine($"a is {a}");
        }
        public void Method02(int a)
        {
            Console.WriteLine($"a is {a * a}");
        }
        public void Method03(int a)
        {
            Console.WriteLine($"a is {a * a * a}");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // インスタンス生成
            Test test = new Test();
            Sample sample = new Sample();

            test.Method01(2);
            test.Method02(2);
            test.Method03(2);

            sample.Method01(2);
            sample.Method02(2);
        }
    }

結果を次に示す。

a is 2
a is 4
a is 8
Sample a is 2
Sample a is 4