[C#] 14. 抽象クラス(abstract)と抽象メソッド(abstract)、そして仮想関数(virtual)


Study / C#    作成日付 : 2019/07/08 23:04:09   修正日付 : 2021/08/20 19:17:30

こんにちは。明月です。


この投稿はC#で使う抽象クラス(abstract)と抽象メソッド(abstract)、そして仮想関数(virtual)に関する説明です。


以前の投稿でクラスの継承、再定義する方法に関して説明したことがあります。

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


クラスの継承は基本的にクラスの機能をそのままに引き続きに継承して新しくクラスを拡張、修正する概念だと説明しました。そのところで、親クラスは継承する前にもインスタンスを生成して使えますが、今回はクラス自体は使えない不完全なクラスでただ継承して再定義してから使うクラスを紹介します。

つまり、クラス自体をインスタンス生成が不可能で継承して再定義して使えるという意味です。

using System;

namespace Example
{
  // 抽象クラス生成
  abstract class AbstractExample
  {
    // 出力関数
    public void Print()
    {
      // コンソール出力 - GetData関数を呼び出す。
      Console.WriteLine("GetData function - " + GetData());
    }
    // GetData関数はこのクラスで定義せずに継承するクラスで強制再定義を指定する。
    protected abstract string GetData();
  }
  // 抽象クラスを継承
  class Example : AbstractExample
  {
    // 抽象クラスからGetData関数が完全に実装されてないので、継承するクラスで必ず再定義すべき。
    protected override string GetData()
    {
      // String返却
      return "Hello world";
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // Exampleクラスのインスタンス生成
      Example ex = new Example();
      // Print関数を呼び出す。
      ex.Print();
 
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例をみればAbstractExampleクラスのGetData関数は宣言だけして内容は実装してないです。

そしてクラスと関数の前にabstractのキーワードを使いました。それならAbstractExampleクラスは抽象クラスになり、GetDataは抽象メソッドになります。

つまり、抽象クラスは未完成クラスで抽象クラス自体はインスタンス生成ができなくて、必ず継承してから使えます。

また、抽象メソッドは継承するクラスで必ず再定義(override)すべきになります。


なので、Exampleクラスで抽象クラスのAbstractExampleクラスを継承する時に抽象メソッドを再定義(override)しました。


この抽象クラスを使う目的は多いクラスの共通部分を一つに取り縛って共通クラスとして実装する時によく使います。

using System;

namespace Example
{
  // 抽象クラス生成
  abstract class AbstractExample
  {
    // 出力関数
    public void Print()
    {
      // コンソール出力 - GetData関数を呼び出す。
      Console.WriteLine("GetData function - " + GetData());
    }
    // GetData関数はこのクラスで定義せずに継承するクラスで強制再定義を指定する。
    protected abstract string GetData();
  }
  // 抽象クラスを継承
  class Example1 : AbstractExample
  {
    // 抽象クラスからGetData関数が完全に実装されてないので、継承するクラスで必ず再定義すべき。
    protected override string GetData()
    {
      // String返却
      return "Example1";
    }
  }
  // 抽象クラスを継承
  class Example2 : AbstractExample
  {
    // 抽象クラスからGetData関数が完全に実装されてないので、継承するクラスで必ず再定義すべき。
    protected override string GetData()
    {
      // String返却
      return "Example2";
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 抽象クラスの指示子で配列を宣言
      AbstractExample[] array = new AbstractExample[]
      {
        // Example1クラスのインスタンス生成
        new Example1(),
        // Example2クラスのインスタンス生成
        new Example2()
      };
      // インスタンスを繰り返しで出力
      foreach (AbstractExample ex in array)
      {
        // 指示子はAbstractExample(Stack)だが、HeapにはExample1あるいはExample2のクラスのインスタンスが割り当てしている。
        ex.Print();
      }
 
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上のMain関数で指示子は抽象クラスでインスタンス生成して割り当てするのは継承したクラスです。

その関係に関しては以前の投稿で説明したことがあるのでご参考してください。

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


またMain関数をみるとforeachでPrint関数を呼び出します。

指示子の抽象クラスには実装はしてないですが、Print関数が宣言されています。なので、Print関数を呼び出して各インスタンスの関数を呼び出すことができます。


結果はPrint関数の文言と各インスタンスで再定義した関数GetDataの結果がコンソールに出力されます。


abstarctクラスで関数が実装されていますが、virtualキーワードを使って再定義ができるような関数指定も可能です。

using System;

namespace Example
{
  // 抽象クラス生成
  abstract class AbstractExample
  {
    // 出力関数
    public void Print()
    {
      /// コンソール出力 - GetData関数を呼び出す。
      Console.WriteLine("GetData function - " + GetData());
    }
    // virtualは仮想関数、抽象関数と違い、実装が可能。
    protected virtual string GetData()
    {
      return "AbstractExample";
    }
  }
  // 抽象クラスを継承
  class Example1 : AbstractExample
  {
    // 抽象クラスからGetData関数が再定義可能。
    protected override string GetData()
    {
      // String返却
      return "Example1";
    }
  }
  // 抽象クラスを継承
  class Example2 : AbstractExample
  {
    // 仮想関数は必ず再定義する必要がない。
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // 抽象クラスの指示子で配列を宣言
      AbstractExample[] array = new AbstractExample[]
      {
        // Example1クラスのインスタンス生成
        new Example1(),
        // Example2クラスのインスタンス生成
        new Example2()
      };
      // インスタンスを繰り返しで出力
      foreach (AbstractExample ex in array)
      {
        // 指示子はAbstractExample(Stack)だが、HeapにはExample1あるいはExample2のクラスのインスタンスが割り当てしている。
        ex.Print();
      }
 
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例はvirtualキーワードを使って実装された関数を再定義できるように設定しました。

Example1クラスでは再定義して結果が再定義したGetData関数の結果が出力されます。

でもExample2クラスの場合は再定義してないですが、そうするとAbstractExampleクラスのGetData関数の結果が出力されますね。


ここまでC#で使う抽象クラス(abstract)と抽象メソッド(abstract)、そして仮想関数(virtual)に関する説明でした。


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

#C#
最新投稿