[C#] 15. インタフェース(interface)


Study / C#    作成日付 : 2019/07/10 00:06:17   修正日付 : 2021/08/26 17:00:48

こんにちは。明月です。


この投稿はC#のインタフェース(interface)に関する説明です。


以前の投稿でインスタンスを生成する方法と継承に関して説明しました。

link - [C#] 11. インスタンスう生成(new)とメモリ割り当て(StackメモリとHeapメモリ)そしてヌル(null)

link - [C#] 13. クラスの継承と再定義(override)する方法、overrideとnewの差異


クラスの共通的な内容を抽象クラスで作成して継承しながらクラスを定義することに関して説明しました。

でも、C#では二つ以上の抽象クラスを継承することができません。

using System;

namespace Example
{
  // ATypeAbstractClass抽象クラス
  abstract class ATypeAbstractClass
  {
    // 抽象メソッド
    public abstract string GetATypeName();
  }
  // BTypeAbstractClass抽象クラス
  abstract class BTypeAbstractClass
  {
    // 抽象メソッド
    public abstract string GetBTypeName();
  }
  // 二つの抽象クラスを継承する。
  class Example : ATypeAbstractClass, BTypeAbstractClass
  {
    // ATypeAbstractClassの抽象クラスを再定義
    public override string GetATypeName()
    {
      return "A";
    }
    // BTypeAbstractClassの抽象クラスを再定義
    public override string GetBTypeName()
    {
      return "B";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("GetATypeName - " + GetATypeName());
      Console.WriteLine("GetBTypeName - " + GetBTypeName());
    }
  }
  class Program
  {
    // 実行関数
    public static void Main(string[] args)
    {
      // インスタンス生成
      Example ex = new Example();
      // 出力関数
      ex.Print();
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


エラーが発生します。理由としては、複数継承エラーです。

複数の継承でも上みたいに使うと問題がなさそうです。でも、Print関数がExampleのクラスではなく、抽象クラスにある関数なら。

継承するクラスではATypeAbstractClassのクラスの関数が使うかBTypeAbstractClassのクラスの関数が使うかを分からなくなります。

それでC#には複数の継承を禁止させています。


上の話はHeapメモリの中でも話です。


それならStackメモリに変数を宣言する時にはオブジェクトの特性上のオブジェクト別に片付ける必要がある時があります。

using System;

namespace Example
{
  // IATypeInterfaceインタフェース
  interface IATypeInterface
  {
    // メソッド定義
    string GetATypeName();
    void Print();
  }
  // IBTypeInterfaceインタフェース
  interface IBTypeInterface
  {
    // メソッド定義
    string GetBTypeName();
    void Print();
  }
  // 二つのインタフェースを継承する。
  class Example : IATypeInterface, IBTypeInterface
  {
    // IATypeInterfaceのインタフェースの関数を再定義
    public string GetATypeName()
    {
      return "A Example";
    }
    // IBTypeInterfaceのインタフェースの関数を再定義
    public string GetBTypeName()
    {
      return "B Example";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("Example - GetATypeName - " + GetATypeName());
      Console.WriteLine("Example - GetBTypeName - " + GetBTypeName());
    }
  }
  // IATypeInterfaceのインタフェースを継承する。
  class AExample : IATypeInterface
  {
    // IATypeInterfaceのインタフェース関数を再定義
    public string GetATypeName()
    {
      return "A - AExample";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("AExample - GetATypeName - " + GetATypeName());
    }
  }
  // IBTypeInterfaceのインタフェースを継承する。
  class BExample : IBTypeInterface
  {
    // IATypeInterfaceのインタフェース関数を再定義
    public string GetBTypeName()
    {
      return "B - BExample";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("BExample - GetBTypeName - " + GetBTypeName());
    }
  }
  class Program
  {
    // 実行関数
    public static void Main(string[] args)
    {
      // インスタンス生成
      Example ex = new Example();
      // 出力関数
      ex.Print();
      // インスタンス生成
      AExample ex1 = new AExample();
      // 出力関数
      ex1.Print();
      BExample ex2 = new BExample();
      // 出力関数
      ex2.Print();
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


Exampleクラスを見ればIATypeInterfaceのインタフェースとIBTypeInterfaceのインタフェースを継承しました。

つまり、IATypeInterfaceのインタフェースの関数とIBTypeInterfaceのインタフェースの関数を再定義すべきです。

それで二つのインタフェースを継承したExampleクラスは二つのインタフェースの関数を再定義しました。

AExampleクラスの場合はIATypeInterfaceインタフェースを継承したので、IATypeInterfaceインタフェースの関数を再定義しました。

BExampleクラスの場合はIBTypeInterfaceインタフェースを継承したので、IBTypeInterfaceインタフェースの関数を再定義しました。

そしてMain関数でインスタンスを生成してPrint関数を呼び出ししました。


インタフェースは抽象クラスと違うのが中でメンバー変数と一般関数を実装することができません。ただ、定義だけします。そうなので、継承する関数では再定義(override)のキーワードを付けてありません。

こんなことを見れば、内部の動作実装やメモリの割り当てのメンバー変数の実装がなく、ただ定義だけあるのでなぜ必要か分からないですね。


しかし、Stackメモリにインスタンスのポインタ値を入れてHeapメモリに割り当てします。つまり、クラスの実体はHeapメモリにあり、それを我々はオブジェクト(Object)といいます。

簡単なプログラムはこのオブジェクトが何個しかないですが、大きいプログラムはこのオブジェクトがすごく多いです。我々はこのオブジェクトを役割や性質により分類する必要があります。

using System;

namespace Example
{
  // IATypeInterfaceのインタフェース
  interface IATypeInterface
  {
    // メソッド定義
    string GetATypeName();
    void Print();
  }
  // IBTypeInterfaceのインタフェース
  interface IBTypeInterface
  {
    // メソッド定義
    string GetBTypeName();
    void Print();
  }
  // インタフェースを継承する。
  class Example : IATypeInterface, IBTypeInterface
  {
    // IATypeInterfaceのインタフェースの関数を定義
    public string GetATypeName()
    {
      return "A Example";
    }
    // IBTypeInterfaceのインタフェースの関数を定義
    public string GetBTypeName()
    {
      return "B Example";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("Example - GetATypeName - " + GetATypeName());
      Console.WriteLine("Example - GetBTypeName - " + GetBTypeName());
    }
  }
  // IATypeInterfaceのインタフェースの関数を定義
  class AExample : IATypeInterface
  {
    // IATypeInterfaceのインタフェースの関数を定義
    public string GetATypeName()
    {
      return "A - AExample";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("AExample - GetATypeName - " + GetATypeName());
    }
  }
  // IBTypeInterfaceのインタフェースの関数を定義
  class BExample : IBTypeInterface
  {
    // IATypeInterfaceのインタフェースの関数を定義
    public string GetBTypeName()
    {
      return "B - BExample";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("BExample - GetBTypeName - " + GetBTypeName());
    }
  }
  class Program
  {
    // 実行関数
    public static void Main(string[] args)
    {
      // インスタンスを生成
      Example ex = new Example();
      AExample ex1 = new AExample();
      BExample ex2 = new BExample();
 
      // IATypeInterfaceのインタフェースのグループ
      IATypeInterface[] aTypes = new IATypeInterface[]
      {
        ex,ex1
      };
      // IBTypeInterfaceのインタフェースグループ
      IBTypeInterface[] bTypes = new IBTypeInterface[]
      {
        ex,ex2
      };
      // IATypeInterfaceのインスタンスグループの出力
      foreach (IATypeInterface a in aTypes)
      {
        a.Print();
      }
      // IBTypeInterfaceのインタフェースグループの出よく
      foreach (IBTypeInterface b in bTypes)
      {
        b.Print();
      }
 
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例みたいにインスタンスの三つを割り当てして各インタフェースグループにより分類してPrint関数を呼び出して出力しました。

using System;

namespace Example
{
  // ITypeInterfaceのインタフェース
  interface ITypeInterface
  {
    // メソッド定義
    string GetTypeName();
    void Print();
  }
  // ITypeInterfaceのインタフェースを継承する。
  class AExample : ITypeInterface
  {
    // ITypeInterfaceのインタフェースの関数を定義
    public string GetTypeName()
    {
      return "A - AExample";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("AExample - GetTypeName - " + GetTypeName());
    }
  }
  // ITypeInterfaceのインタフェースを継承する。
  class BExample : ITypeInterface
  {
    // ITypeInterfaceのインタフェースの関数を定義
    public string GetTypeName()
    {
      return "B - BExample";
    }
    // 出力関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("BExample - GetTypeName - " + GetTypeName());
    }
  }
  class Program
  {
    // パラメータにより割り当てするクラスが違う。
    public static ITypeInterface GetTypeClass(bool type)
    {
      // trueならAExampleクラスを割り当て
      if (type)
      {
        return new AExample();
      }
      // falseならBExampleクラスを割り当て
      else
      {
        return new BExample();
      }
    }
    // 実行関数
    public static void Main(string[] args)
    {
      // パラメータでtrueを渡したのでAExampleクラスのインスタンスが生成
      ITypeInterface type = GetTypeClass(true);
      // Print関数を実行
      type.Print();

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


関数を利用してパラメータによりインスタンスの生成を別にすることができます。

GetTypeClass関数によりtrueの場合はAExampleクラスのインスタンスを生成するし、falseの場合はBExampleクラスのインスタンスを生成します。


このようにインタフェースは直接にインスタンスを生成することではないですが、生成されたインスタンスをインタフェースにより分類ができます。

そしてインタフェースはOOPのデザインパターン(プロジェクト設計)の基礎になります。


ここまでC#のインタフェース(interface)に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿