Override キーワードと New キーワードによるバージョン管理 (C# プログラミング ガイド)
C# 言語は、異なるライブラリの基本クラスと派生クラスの間でも、下位互換性を維持して改良できるようにバージョン管理がデザインされています。 たとえば、C# では、派生クラスのメンバーと同じ名前の新しいメンバーを基本クラスに導入することが完全にサポートされているため、予期しない動作が起こることはありません。 また、メソッドが継承メソッドをオーバーライドするかどうか、またはメソッドが同じ名前の継承メソッドを隠す新しいメソッドかどうかをクラスに明示的に記述する必要があります。
C# では、基本クラスのメソッドと同じ名前のメソッドを派生クラスに含めることができます。
基本クラスのメソッドは、仮想と定義する必要があります。
派生クラスのメソッドの前に new キーワードや override キーワードが記述されていない場合、コンパイラは警告メッセージを表示し、そのメソッドは new キーワードが存在するように動作します。
派生クラスのメソッドの前に new キーワードが記述されている場合、そのメソッドは、基本クラスのメソッドから独立したものとして定義されます。
派生クラスのメソッドの前に override キーワードが記述されている場合、派生クラスのオブジェクトは、基本クラスのメソッドではなく、派生クラスのメソッドを呼び出します。
base キーワードを使用すると、派生クラスの中から基本クラスのメソッドを呼び出すことができます。
override、virtual、および new の各キーワードは、プロパティ、インデクサー、およびイベントにも適用できます。
C# のメソッドは、既定で非仮想メソッドです。 メソッドが仮想と宣言されている場合、そのメソッドを継承するクラスでは、固有のバージョンを実装できます。 メソッドを仮想メソッドにするには、virtual 修飾子を基本クラスのメソッド宣言で使用します。 その場合、派生クラスでは、override キーワードを使用して基本クラスの仮想メソッドをオーバーライドすることも、new キーワードを使用して基本クラスの仮想メソッドを隠すこともできます。 override キーワードも new キーワードも指定しない場合、コンパイラは警告メッセージを表示し、派生クラスのメソッドは基本クラスのメソッドを隠します。
このことを実際に例を挙げて説明します。GraphicsClass という名前のクラスを A 社で作成しており、このクラスを自分のプログラムで使用するとします。 GraphicsClass を次に示します。
class GraphicsClass
{
public virtual void DrawLine() { }
public virtual void DrawPoint() { }
}
会社でこのクラスを使用しているので、このクラスから次のように独自のクラスを派生し、新しいメソッドを追加します。
class YourDerivedGraphicsClass : GraphicsClass
{
public void DrawRectangle() { }
}
作成したアプリケーションは、A 社が次のコードに示すような GraphicsClass の新バージョンをリリースするまで、問題なく使用できます。
class GraphicsClass
{
public virtual void DrawLine() { }
public virtual void DrawPoint() { }
public virtual void DrawRectangle() { }
}
GraphicsClass の新バージョンには、DrawRectangle という名前のメソッドが含まれています。 最初は、何の問題も発生しません。 新しいバージョンは、古いバージョンとバイナリ互換性があります。 新しいクラスがコンピューター システムにインストールされても、配置済みのソフトウェアは引き続き正常に動作します。 DrawRectangle メソッドへの既存の呼び出しは、派生クラスのバージョンを参照し続けます。
ただし、GraphicsClass の新バージョンを使ってアプリケーションを再コンパイルすると、コンパイラが警告メッセージを表示します (CS0108)。 この警告の内容は、DrawRectangle メソッドをアプリケーションでどのように動作させるかを検討する必要があるというものです。
自分のメソッドで新しい基本クラスのメソッドをオーバーライドする場合は、次のように override キーワードを使用します。
class YourDerivedGraphicsClass : GraphicsClass
{
public override void DrawRectangle() { }
}
override キーワードにより、YourDerivedGraphicsClass から派生したオブジェクトは、確実に DrawRectangle の派生クラス バージョンを使用します。 YourDerivedGraphicsClass から派生したオブジェクトは、次のように base キーワードを使用して、DrawRectangle の基本クラス バージョンに引き続きアクセスできます。
base.DrawRectangle();
自分のメソッドが新しい基本クラスのメソッドをオーバーライドしないようにする場合は、次の点に注意する必要があります。 2 つのメソッドを混同しないようにするために、自分のメソッドの名前を変更します。 この作業には時間がかかり、エラーが発生しやすいので、場合によっては適切でないことがあります。 プロジェクトが比較的小規模である場合は、Visual Studio のリファクタリング オプションを使用して、メソッドの名前を変更できます。 詳細については、「クラスおよび型のリファクタリング (クラス デザイナー)」を参照してください。
または、次のように派生クラスの定義で new キーワードを使用して警告を表示させないようにすることもできます。
class YourDerivedGraphicsClass : GraphicsClass
{
public new void DrawRectangle() { }
}
new キーワードを使用すると、基本クラスに含まれている定義が派生クラスの定義によって隠されることがコンパイラに通知されます。 これが既定の動作です。
オーバーライドとメソッドの選択
クラスでメソッドに名前を付けると、名前が同じで、渡されるパラメーターと互換のパラメーターを持つ 2 つのメソッドが存在する場合など、複数のメソッドが呼び出しに対応する場合に、呼び出すのに最適なメソッドを C# コンパイラが選択します。 次の 2 つのメソッドは互換です。
public class Derived : Base
{
public override void DoWork(int param) { }
public void DoWork(double param) { }
}
Derived のインスタンスで DoWork が呼び出されると、C# コンパイラは最初に、Derived で当初宣言された DoWork のバージョンと互換性のある呼び出しを実行します。 オーバーライド メソッドは、クラスで宣言されたものと見なされません。これらは、基本クラスで宣言されたメソッドの新しい実装です。 C# コンパイラは、Derived の元のメソッドにメソッド呼び出しを一致させることができない場合に限り、名前が同じで互換のパラメーターを持つ、オーバーライドされたメソッドに呼び出しを一致させようとします。 次に例を示します。
int val = 5;
Derived d = new Derived();
d.DoWork(val); // Calls DoWork(double).
変数 val は、暗黙的に double に変換できるので、C# コンパイラは、DoWork(int) の代わりに DoWork(double) を呼び出します。 これを回避する方法は 2 つあります。 1 つは、仮想メソッドと同じ名前を付けて新しいメソッドを宣言することを避ける方法です。 もう 1 つは、Derived のインスタンスを Base にキャストすることにより、C# コンパイラに対して、基本クラスのメソッド リストを検索して、仮想メソッドを呼び出すように指示する方法です。 メソッドが仮想であるため、Derived の DoWork(int) の実装が呼び出されます。 次に例を示します。
((Base)d).DoWork(val); // Calls DoWork(int) on Derived.