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


Study / C#    作成日付 : 2019/07/08 22:55:00   修正日付 : 2021/08/18 15:17:07

こんにちは。明月です。


この投稿はC#でクラスの継承と再定義(override)する方法、overrideとnewの差異に関する説明です。


プロジェクトでクラスを作成する時に既存にあるクラスと似てますが、少しずつ違うクラスを作成する場合があります。また、クラスを修正する時に初期開発の場合はただ修正して使いますが、もし運用中なら既存クラスを勝手に修正すると参照するところでエラーが発生する場合があるので、既存のクラスと似てるクラスを新しく作成するしかないです。

その時に一番簡単な方法はただクラスをコピペして作成したら楽です。


例として、我々が10個のクラスをコピペしてクラスを作成しました。でも、コピペした元のクラスでエラー(バグ)が発生しました。その場合はコピペしたクラスを修正するために10回に修正するべきになります。

簡単なプログラムならそれでもできる範囲ですが、仕様が大きいプログラムなら10個だけではなく、100個以上になる可能性もあるので、それをすべて修正することは簡単な話ではありません。参考に実務でもこんなに作成したケースが多いですね。


なので、プログラム言語では既存のクラスの機能をそのままに持ってきて拡張、修正する機能がありますが、それを継承といいます。

using System;

namespace Example
{
  // クラス生成
  class Example
  {
    // 出力関数
    public void Print()
    {
      // コンソールに出力
      Console.WriteLine("Call Print function");
    }
  }
  // Exampleクラスを継承、派生クラスという。
  class CopyExample : Example
  {
    // 出力関数
    public void Print2()
    {
      // コンソールに出力
      Console.WriteLine("Call Print2 function");
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // インスタンス生成
      CopyExample clz = new CopyExample();
      // 継承すれば親クラスの機能をすべて使うことができる。
      clz.Print();
      // 追加された関数
      clz.Print2();
 
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例をみればCopyExampleというクラスはExampleのクラスから継承しました。クラスの継承はクラスを生成する時に隣でコロン(:)を付けて親クラスを指定するとクラスが継承になります。

実際にMain関数でCopyExampleクラスのインスタンスを生成して関数を呼び出すとExampleの機能をそのままに使えることを確認できます。


クラスを継承すると親クラスの機能をそのままに持ってきて関数やメンバー変数を新しく追加することができます。しかし、親クラスの関数や変数でそのままに使わなくて再定義する時もあります。

using System;

namespace Example
{
  // クラス生成
  class Example
  {
    // 出力関数
    public void Print()
    {
      // コンソールに出力
      Console.WriteLine("Call Print function");
    }
  }
  // Exampleクラスを継承、派生クラスという。
  class CopyExample : Example
  {
    // 出力関数
    public void Print2()
    {
      // コンソールに出力
      Console.WriteLine("Call Print2 function");
    }
    // 返却タイプ、関数名、パラメータが同じ名に作成すると再定義になる。アクセス修飾子の次に再定義するキーワードnewを入れる。
    public new void Print()
    {
      // コンソールに出力
      Console.WriteLine("Call new Print function");
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // インスタンスを生成
      CopyExample clz = new CopyExample();
      // Print関数を再定義したことを呼び出す。
      clz.Print();
      // 追加された関数
      clz.Print2();
 
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例でCopyExampleクラスでnewキーワードを使って関数を再定義しました。

結果は親クラスのPrintの値ではなく、再定義した結果がコンソールに表示されます。


実は再定義キーワードはoverrideです。このoverrideは親クラスで再定義を許した場合(abstractやvirtual)に使います。しかし上みたいに親クラスの一般関数を再定義する時にはnewキーワードを使ったらよいです。

using System;

namespace Example
{
  // クラス生成
  class Example
  {
    // 出力関数(仮想関数)
    public virtual void Print()
    {
      // コンソールに出力
      Console.WriteLine("Call Print function");
    }
  }
  // Exampleクラスを継承、派生クラスという。
  class CopyExample : Example
  {
    // 出力関数
    public void Print2()
    {
      // コンソールに出力
      Console.WriteLine("Call Print2 function");
    }
    // 返却タイプ、関数名、パラメータが同じ名に作成すると再定義になる。アクセス修飾子の次に再定義するキーワードoverrideを入れる。
    public override void Print()
    {
      // コンソールに出力
      Console.WriteLine("Call new Print function");
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // インスタンスを生成
      CopyExample clz = new CopyExample();
      // Print関数を再定義したことを呼び出す。
      clz.Print();
      // 追加された関数
      clz.Print2();
 
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例はExampleクラスでPrint関数にvirtualキーワードを入れて再定義ができるように設定しました。

その後、継承したCopyExampleクラスでoverrideを利用してPrint関数を再定義しました。参考に親クラスでvirtual設定しなかった関数をoverrideするとエラーが発生します。

結果は再定義した関数の内容がコンソールに出力します。


overrideとnewは同じく再定義していることに見えますが、根本的な差異があります。

using System;

namespace Example
{
  // クラス生成
  class Example
  {
    // 出力関数(仮想関数)
    public virtual void Print()
    {
      // コンソールに出力
      Console.WriteLine("Call Print function");
    }
  }
  // Exampleクラスを継承
  class CopyExample1 : Example
  {
    // override再定義
    public override void Print()
    {
      // コンソールに出力
      Console.WriteLine("Call new Print function");
    }
  }
   // Exampleクラスを継承
  class CopyExample2 : Example
  {
    // new再定義
    public new void Print()
    {
      // コンソールに出力
      Console.WriteLine("Call new Print function");
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // インスタンスを生成
      Example clz1 = new CopyExample1();
      // 関数呼び出す。
      clz1.Print();
 
      // インスタンスを生成
      Example clz2 = new CopyExample2();
      // 関数呼び出す。
      clz2.Print();
 
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例でExampleを変数タイプに設定してCopyExample1とCopyExample2クラスのインスタンスを生成しました。

ここで再定義をoverrideしたクラスは派生クラスの再定義した関数でコンソールに出力しますが、newで再定義したクラスはExampleクラスの関数が呼び出しました。


改めてまとめるとoverrideにした関数は確実に再定義しました。つまり、HeapのインスタンスでPrint関数が一つにあることを予想できます。

でもnewで再定義したことはインスタンス中でPrint関数が二つがあることです。つまり、変数タイプをExampleにするとExampleのPrint関数が呼び出すし、CopyExample2にするとCopyExample2のPrint関数が呼び出します。


ここまでC#でクラスの継承と再定義(override)する方法、overrideとnewの差異に関する説明でした。


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

#C#
最新投稿