C Sharpの基礎 - ポリモーフィズム

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
2020年2月3日 (月) 16:39時点におけるWiki (トーク | 投稿記録)による版 (ページの作成:「== 概要 == ポリモーフィズムとはオブジェクト指向プログラミングの概念の1つである。多態性・多様性などと訳される。<br> 同…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
ナビゲーションに移動 検索に移動

概要

ポリモーフィズムとはオブジェクト指向プログラミングの概念の1つである。多態性・多様性などと訳される。
同じ名前のメソッドを継承したクラスで使用できるようにし、そのメソッドを通して、暗黙的に複数のインスタンスの動作を切り替えることができるようにする。

C#でポリモーフィズムを実現するには、以下の2つ方法がある。

  • 継承を使う方法
  • インターフェースを使う方法



継承を使用したポリモーフィズム

まず、Superという基底クラスを定義する。
abstractが付加されたクラスは抽象クラスとなり、インスタンス化できないクラスである。
抽象クラスは、継承したサブクラスを定義することが前提となる。

 // Super class
 public abstract class Super
 {
    public virtual string Hello()
    {
        return "";
    }
 }
 
 // Derived 1 class
 public class Derived1 : Super
 {
    public override string Hello()
    {
        return "Hello";
    }
 }
 
 public class Derived2 : Super
 {
    public override string Hello()
    {
        return "こんにちは";
    }
 }


各サブクラスでは、overrideキーワードを使用して、Hello()メソッドを再定義している。
これにより、Hello()メソッドを持っているサブクラスを統一的に扱うことができるようになる。

実際に、上記のクラスを使用したコードは以下のようになる。
各サブクラスのインスタンスを格納したList<T>の要素が、それぞれの継承元であるSuperクラスである。
foreachブロックでは、Derived1もDerived2も同じSuperクラスとみなされている。

しかし、呼び出されるHello()メソッドは、SuperクラスのHello()メソッドではなく、実際のインスタンスのものとなる。
つまり、実際の型がDerived1ならDerived1、Derived2ならDerived2のHello()メソッドが呼び出されている。

抽象クラスを使用すると、異なる型のオブジェクトを同一視し、そのオブジェクトの型によって動作が切り替えることができるようになる。
ポリモーフィズムでは、静的型付け言語の厳密性を保ったまま、動的型付け言語の柔軟性を得ることができる。

 using System;
 using System.Collections.Generic;
 
 public class Program
 {
    static void Main()
    {
       var Deriveds = new List<Super>(){new Derived1(), new Derived2()};
       foreach (Derived character in Deriveds)
       {
          Console.WriteLine(Derived.Hello());
       }
    }
 }
// 結果
Hello
こんにちは



インターフェースを使ったポリモーフィズム

インターフェースを使用した抽象クラスでも同様なことが可能である。
インターフェースは、製品の規格のようなもので、プロパティやメソッドの呼び出し方だけを定めたものである。

 // IGreeting interface
 interface IGreeting
 {
    string Hello();
 }


下記のIGreetingを継承したクラスでは、Hello()メソッドを必ず定義しなければならないと定められている。
また、インターフェースでは、public等のアクセス修飾子は付けることはできない。(継承したクラスで付加する)

次に、IGreetingインターフェイスを実装した各サブクラスを定義する。
インターフェースのメソッドやプロパティの具体的な動作は、ここに記述することになる。

 // IGreetingインターフェースの実装
 public class Derived1 : IGreeting
 {
    public string Hello()
    {
       return "Hello\n";
    }
 }
 
 public class Derived2 : IGreeting
 {
    public string Hello()
    {
       return "こんにちは\n";
    }
 }


上記の各クラスを使用したコードは以下のようになる。
呼び出し側のコードは、List<T>の要素がIGreetingに変わっただけでスーパークラスを継承した場合と同じである。

 using System;
 using System.Collections.Generic;
 
 public class Program
 {
    static void Main()
    {
       var Deriveds = new List<IGreeting>() {new Derived1(), new Derived2()};
       foreach (IGreeting Derive in Deriveds)
       {
            Console.WriteLine(Derive.Hello());
       }
    }
 }



その他

抽象メソッドの修飾子であるvirtualとabstractについて記載する。

抽象メソッドの修飾子がvirtualの場合、継承先のクラスでオーバーライドしなくても構わない。
抽象メソッドの修飾子がabstractの場合、実際の処理を記述しないが、継承先のクラスにて必ずオーバーライドして実際の処理を定義する必要がある。

 // abstractを使用した抽象メソッド
 public abstract class Greeting
 {
    public abstract string Hello();
 }