.NET Visual C#言語入門 > 継承
のぶ亭『プログラミングの相談窓口』 … 様々なプログラミング問題を個別対応致します |
継承
クラスは、別のクラスを継承できます。この処理を行うには、次のようにクラスの宣言時にクラス名の後にコロンを配置し、コロンの後に継承元のクラス (基本クラス) の名前を指定します。
public class A { public A() { } } public class B : A { public B() { } }
これで新しいクラス (派生クラス) は、基本クラスの非プライベート・データと動作をすべて取得し、さらに独自に定義した他のデータと動作も取得します。新しいクラスの有効な型は2つになります。1つは新しいクラスの型で、もう1つは継承元のクラスの型です。
上の例では、クラスBは、BでもAでも有効です。Bオブジェクトにアクセスするときは、キャスト操作を使用して、B オブジェクトを A オブジェクトに変換できます。B オブジェクトはキャストによって変更されませんが、B オブジェクトのビューは、A のデータと動作に制限されるようになります。B を A にキャストした後、この A は B にキャスト・バックできます。その際、B にキャストできるのは、A のすべてのインスタンスではなく、Bの実際のインスタンスだけに限定されます。クラスBにB型としてアクセスすると、クラスAとクラスBのデータと動作を取得します。
オブジェクトが複数の型を表す機能をポリモーフィズムと言います。詳細については、「ポリモーフィズム」を参照してください。キャストの詳細については、「キャスト」を参照してください。
構造体は、他の構造体やクラスを継承できません。
クラスと構造体は共に 1 つ以上のインターフェイスを継承できます。詳細については、「インターフェイス」を参照してください。
抽象クラスとシール クラス、およびクラス メンバ
abstract キーワードを使用すると、継承専用のクラスとクラス メンバを作成し、派生した非抽象クラスの機能を定義できます。また、sealed キーワードを使用すると、既に virtual とマークされているクラスや特定のクラス メンバを継承しないようにできます。
抽象クラスと抽象クラス メンバ
クラスは抽象として宣言できます。このように宣言するには、クラス定義で class キーワードの前に abstract キーワードを配置します。次に例を示します。
public abstract class A { // Class members here. }
抽象クラスはインスタンス化できません。抽象クラスの目的は、複数の派生クラスで共有できる基本クラスの共通の定義を提供することです。たとえば、クラス ライブラリでは、その多くの関数のパラメータとして使用される抽象クラスを定義できます。このライブラリを使用する場合は、派生クラスを作成してクラスの独自の実装を提供する必要があります。
抽象クラスでは、抽象メソッドも定義できます。抽象メソッドを定義するには、メソッドの戻り値の型の前に abstract キーワードを記述します。次に例を示します。
public abstract class A { public abstract void DoWork(int i); }
抽象メソッドには実装がないので、メソッド定義の後に、通常のメソッド ブロックの代わりにセミコロン (;) を配置します。抽象クラスの派生クラスでは、すべての抽象メソッドを実装する必要があります。抽象クラスが基本クラスから仮想メソッドを継承した場合は、この抽象クラスでは抽象メソッドで仮想メソッドをオーバーライドできます。次に例を示します。
// compile with: /target:library public class D { public virtual void DoWork(int i) { // Original implementation. } } public abstract class E : D { public abstract override void DoWork(int i); } public class F : E { public override void DoWork(int i) { // New implementation. } }
仮想メソッドが抽象として宣言されている場合は、抽象クラスを継承するすべてのクラスに対しても仮想です。抽象メソッドを継承するクラスでは、そのメソッドの元の実装にアクセスできません。上の例では、クラス F の DoWork は、クラス D の DoWork を呼び出すことができません。このようにして抽象クラスは、派生クラスに対し、仮想メソッドの新しいメソッド実装を強制的に提供させることができます。
シール クラスとシール クラス メンバ
クラスは、シールとして宣言できます。このように宣言するには、クラス定義で class キーワードの前に sealed キーワードを配置します。次に例を示します。
public sealed class D { // Class members here. }
シール クラスは、基本クラスとして使用できません。このため、シール クラスは抽象クラスになることもできません。シール クラスは、主に派生を防ぐために使用します。シール クラスは基本クラスとして使用できないので、実行時の最適化で、シール クラス メンバを多少高速に呼び出すことができる場合があります。
基本クラスの仮想メンバをオーバーライドしている派生クラスのクラス メンバ、メソッド、フィールド、プロパティ、またはイベントでは、そのメンバをシールとして宣言できます。これにより、その後の派生クラスでは、メンバの仮想性が無効になります。このように宣言するには、クラス メンバ宣言で override キーワードの前に sealed キーワードを配置します。次に例を示します。
public class D : C { public sealed override void DoWork() { } }
ポリモーフィズム
継承を通じて、クラスは、固有の型、基本型、またはインターフェイスを実装する場合はインターフェイス型など、複数の型として使用できます。これをポリモーフィズムと言います。C# では、すべての型がポリモーフィックです。型は固有の型として使用することも、Object インスタンスとして使用することもできますが、これは、どの型も Object を基本型として自動的に取り扱うからです。
ポリモーフィズムは、派生クラスだけでなく、基本クラスに対しても重要です。base クラスを使用しているときは、実際には、基本クラス型にキャストされた派生クラスのオブジェクトを使用していることがあります。基本クラスを設計するときは、派生型で変更される可能性がある部分を予測できます。たとえば、自動車の基本クラスの場合は、問題の自動車がミニバンまたはコンバーチブルのときに変更する動作を含めることができます。その基本クラスでクラス メンバを virtual とマークすると、コンバーチブルやミニバンを表す派生クラスがその動作をオーバーライドできるようになります。
ポリモーフィズムの概要
基本クラスから派生クラスを継承すると、派生クラスは、基本クラスのすべてのメソッド、フィールド、プロパティ、およびイベントを継承します。基本クラスのデータと動作を変更するには、2 つの方法があります。1 つは、基本メンバを新しい派生メンバに置き換える方法で、もう 1 つは、仮想基本メンバをオーバーライドする方法です。
基本クラスのメンバを新しい派生メンバに置き換えるには、new キーワードを使用する必要があります。基本クラスでメソッド、フィールド、またはプロパティを定義している場合、new キーワードを使用して、該当するメソッド、フィールド、またはプロパティの新しい定義を派生クラスで作成します。new キーワードは、置き換えられるクラス メンバの戻り値の型の前に配置します。次に例を示します。
public class BaseClass { public void DoWork() { } public int WorkField; public int WorkProperty { get { return 0; } } } public class DerivedClass : BaseClass { public new void DoWork() { } public new int WorkField; public new int WorkProperty { get { return 0; } } }
new キーワードを使用すると、置き換えられた基本クラス メンバの代わりに新しいクラス メンバが呼び出されます。置き換えられた基本クラス メンバは、隠しメンバと呼ばれます。隠しクラス メンバは、派生クラスのインスタンスが基本クラスのインスタンスにキャストされた場合に呼び出すことができます。次に例を示します。
DerivedClass B = new DerivedClass(); B.DoWork(); // Calls the new method. BaseClass A = (BaseClass)B; A.DoWork(); // Calls the old method.
派生クラスのインスタンスが基本クラスのクラス メンバを完全に継承できるようにするには、基本クラスでそのメンバを virtual として宣言する必要があります。そのように宣言するには、そのメンバの戻り値の型の前に virtual キーワードを追加します。これにより、派生クラスでは、new の代わりに override キーワードを使用して、基本クラスの実装を派生クラス固有の実装に置き換えることができます。次に例を示します。
public class BaseClass { public virtual void DoWork() { } public virtual int WorkProperty { get { return 0; } } } public class DerivedClass : BaseClass { public override void DoWork() { } public override int WorkProperty { get { return 0; } } }
フィールドは仮想メンバにできません。仮想メンバにできるのは、メソッド、プロパティ、イベント、およびインデクサだけに限られます。派生クラスが仮想メンバをオーバーライドすると、派生クラスのメンバは、そのクラスのインスタンスが基本クラスのインスタンスとしてアクセスされるときでも呼び出されます。次に例を示します。
DerivedClass B = new DerivedClass(); B.DoWork(); // Calls the new method. BaseClass A = (BaseClass)B; A.DoWork(); // Also calls the new method.
仮想メソッドや仮想プロパティにより、今後の拡張を前もって計画することできます。仮想メンバは、呼び出し元が使用している型とは無関係に呼び出されるので、基本クラスの明白な動作を派生クラスで完全に変更できるようにします。
仮想メンバは、それを最初に宣言したクラスとの間でどれほど多くのクラスが宣言されても、いつまでも仮想のままです。たとえば、クラス A が仮想メンバを宣言し、クラス B がクラス A から派生し、クラス C が クラス B から派生した場合、クラス C は仮想メンバを継承し、クラス B がその仮想メンバのオーバーライドを宣言した場合でも、そのメンバをオーバーライドできます。次に例を示します。
public class A { public virtual void DoWork() { } } public class B : A { public override void DoWork() { } }
public class C : B { public override void DoWork() { } }
派生クラスでは、オーバーライドを sealed と宣言して仮想継承を中止できます。この場合、クラス メンバの宣言で、override キーワードの前に sealed キーワードを配置する必要があります。次に例を示します。
public class C : B { public sealed override void DoWork() { } }
上の例では、DoWork メソッドは C から派生したすべてのクラスに対して仮想でなくなります。ただし、C のインスタンスに対しては、それが B 型や A 型にキャストされた場合でも、依然として仮想です。シール メソッドは、次のコード例に示すように、派生クラスで new キーワードを使用することで置き換えることができます。
public class D : C { public new void DoWork() { } }
このコード例では、DoWork が、D 型の変数を使用して D で呼び出されると、新しい DoWork が呼び出されます。また、C 型、B 型、または A 型の変数を使用して D のインスタンスにアクセスした場合、DoWork への呼び出しは、仮想継承の規則に従って、クラス C の DoWork の実装に転送されます。
メソッドやプロパティを置き換えたり、オーバーライドしたりした派生クラスでは、base キーワードを使用することで、基本クラスのメソッドやプロパティにアクセスできます。次に例を示します。
public class A { public virtual void DoWork() { } } public class B : A { public override void DoWork() { } }
public class C : B { public override void DoWork() { base.DoWork(); // DoWork behavior specific to C goes here: // ... } }
メモ |
---|
仮想メンバの場合、その固有の実装で base を使用して、その仮想メンバの基本クラス実装を呼び出すことをお勧めします。基本クラスの動作を実現することで、派生クラスは、その固有の動作を実装することに集中できます。基本クラス実装を呼び出さない場合は、基本クラスの動作と互換性のある動作を派生クラスで実現する必要があります。 |
インターフェイス
インターフェイスは、interface キーワードを使用して定義します。次に例を示します。
interface IComparable
{
int CompareTo(object obj);
}
インターフェイスは、任意のクラスまたは構造体に属する関連動作のグループを表します。インターフェイスは、メソッド、プロパティ、イベント、インデクサ、またはこれら 4 つのメンバの型を自由に組み合わせて構成できます。インターフェイスには、フィールドを含めることができません。インターフェイス メンバは自動的にパブリックになります。
クラスが基本クラスや構造体を継承できるのと同じように、クラスも構造体もインターフェイスを継承できますが、この場合、次の2点が異なります。
- クラスや構造体は、複数のインターフェイスを継承できます。
- クラスや構造体がインターフェイスを継承するときは、メンバ定義は継承しますが、実装は継承しません。次に例を示します。
public class Minivan : Car, IComparable { public int CompareTo(object obj) { //implementation of CompareTo return 0; //if the Minivans are equal } }
インターフェイス メンバを実装するには、クラスの対応するメンバは、パブリックかつ非静的であり、その名前とシグネチャがインターフェイス メンバと一致する必要があります。クラスのプロパティとインデクサでは、インターフェイスで定義されているプロパティやインデクサに追加のアクセサを定義できます。たとえば、インターフェイスでは、get アクセサを持つプロパティを宣言できますが、このインターフェイスを実装するクラスでは、get と set の両方のアクセサを持つ同じプロパティを宣言できます。ただし、プロパティやインデクサで明示的な実装を使用する場合は、アクセサを一致させる必要があります。
インターフェイスとインターフェイス メンバは抽象であり、インターフェイスは既定の実装を提供しません。
IComparable インターフェイスは、オブジェクトがそれ自体を同じ型の他のオブジェクトと比較できることをオブジェクトのユーザーに通知します。このインターフェイスのユーザーは、このような比較がどのように実装されるかを知る必要がありません。
インターフェイスは、他のインターフェイスを継承できます。クラスでは、継承した基本クラスやインターフェイスを介して、インターフェイスを繰り返し継承できます。このとき、クラスは、インターフェイスが新しいクラスの一部として宣言されている場合、インターフェイスを 1 回のみ実装できます。継承されたインターフェイスが新しいクラスの一部として宣言されていない場合、インターフェイスの実装は、それを宣言した基本クラスによって提供されます。基本クラスでは、仮想メンバを使ってインターフェイス メンバを実装できます。その場合、インターフェイスを継承するクラスでは、仮想メンバをオーバーライドしてインターフェイスの動作を変更できます。
インターフェイスの概要
- インターフェイスは抽象基本クラスに似ています。インターフェイスを継承する非抽象型は、すべてのインターフェイス メンバを実装する必要があります。
- インターフェイスは直接インスタンス化できません。
- インターフェイスには、イベント、インデクサ、メソッド、およびプロパティを含めることができます。
- インターフェイスには、メソッドの実装が含まれません。
- クラスと構造体は、複数のインターフェイスを継承できます。
- インターフェイス自体が複数のインターフェイスを継承できます。