継承を上手に使ってクラスを作ると,クラスの外側のプログラミングでも楽ができます。今回は,クラスを利用する側の視点から継承を有効的に使う方法を紹介します。

 前々回,前回と2回に渡って「クラス」の作り方を紹介してきました。そこでは,クラスの持つ機能を「どのようにして実装していくのか」また「どのように拡張していくのか」といった,クラスを内側から見たときのプログラミング方法が中心でした。

 前回で紹介した「継承」を使うと,元のクラスに手を加えることなく,元のクラスに新しい機能(メソッドやプロパティ)を追加した別のクラスを作ることが簡単にできました。一つのクラスを親として,そこからたくさんの子クラス(サブクラス)を作ることも簡単です。

 こうして作ったクラスは,アプリケーションの部品として利用することになります。これは,クラスを外側から見たときのプログラミングと言えるでしょう。

 クラスを外から使うためには,個々のクラスの使い方を知っていなければなりません。扱うクラスが多くなればなるほど,覚えなければならないことが増えていくことになります。そこで重要になってくるのが継承の使い方です。継承を上手に使ってクラスを作ると,クラスを使うアプリケーション,つまりクラスの外側のプログラミングでも楽ができます。今回は,クラスを利用する側の視点から継承を有効的に使う方法を紹介します。

継承で作ったクラスは親クラスとしても扱える

 これまでに何度か取り上げたように,C#では常に型を意識してプログラムを書かなければなりません。つまり,ある型の変数に別の型のインスタンスを入れることは基本的にできません。例えば,

 MyClass mc = new OtherClass();

というコードは,MyClass型の変数にOtherClass型のインスタンスを生成して格納しようとしています。このコードをコンパイルすると「型'OtherClass'を型'MyClass' に暗黙的に変換できません。」というエラーが出ます。まわりくどいメッセージですが,要するに変数mcの型と,newキーワードで生成されたインスタンスの型が一致していないから代入できないという意味です。

 しかし,このような文が正しく動作するケースもあります。それは,二つのクラスが継承関係にある場合です。例えば,MyClass型を継承して作られたNewClass型がある場合(リスト1(a)),NewClass型のインスタンスはMyClass型の変数に格納できます。つまり(b)のような文は正常にコンパイルされます。

リスト1●異なる型でも継承で親子関係にあれば代入できる
リスト1●異なる型でも継承で親子関係にあれば代入できる

異なるクラスの使い方を共通化できる

 このようなプログラムを書けることが一体どんな役に立つのでしょうか?それは,クラスを利用する側の視点から見たときに,複数のクラスを扱うコードを共通化できるということなのです。

 MyClassクラスを継承して作られたNewClassクラスは,MyClassの機能を引き継ぎます。さらにもう一つ,NewClassクラスを継承してSubClassクラスを作成した場合も,やはりSubClassはMyClassの機能を引き継ぎます。

 これら三つのクラスMyClass,NewClass,SubClassすべてを使うアプリケーションを作るとき,変数とインスタンスの型を完全に一致させなければならないとしたら,それぞれのクラスの型に合わせた変数を用意しなければなりません。そして,それぞれの変数にその型のインスタンスを格納して,メソッドやプロパティを呼び出すことになります。

 このとき,アプリケーションが呼び出したいのが,親クラスのMyClassで定義されているメソッドやプロパティだったとしましょう。実際のプログラミングでもこのような状況はよくあります。親クラスで定義されているメソッドやプロパティなので,それらはそこから派生した二つのクラス(NewClassとSubClass)を含め,三つのクラスすべてに共通した機能です。アプリケーションがしようとしていることは三つのクラスが持っている同じ名前のメソッドやプロパティを呼び出すだけなのに,それぞれの型に合わせてクラスを操作するコードも三つ書かなければならないわけです。子クラスが三つ,四つと増えれば増えるほどそれに対応するコードの量も増えます。クラスを使う処理に変更が発生すれば全部のコードに対して書き直しが必要になります。ムダであるばかりか少しの変更でも多くの修正が必要な,保守性の悪いコードになってしまうのです。

 もちろん,実際にそのようなコードを書く必要はありません。クラスが継承関係にあるなら子クラスのインスタンスを親クラスの変数に格納できるからです。単に格納できるだけではなく,親クラスの変数に格納した子クラスのインスタンスはすべて親クラスの変数を通して共通の手順で扱うことができます。とはいうものの,単にクラスを継承しただけではこの恩恵を受けることはできません。前回に少しだけ紹介した「仮想メソッド」を使った細工が必要です。

 まず,仮想メソッドを使わずに単なる継承で子クラスを作った場合に何が起こるかを見てみましょう。MyClass型のクラス定義に「MyClassです」というメッセージを表示するメソッドを追加してみましょう(リスト2(a))。NewClassクラスには何も手を加えません。この状態で,NewClass型のインスタンスを,NewClass型の変数に格納してShowMyNameメソッドを呼び出すコード(b)と,MyClass型の変数に格納してShowMy-Nameメソッドを呼び出すコード(c)を実行してみます。

リスト2●継承で親クラスから受け継いだメソッドを呼び出す
リスト2●継承で親クラスから受け継いだメソッドを呼び出す

 変数の型がNewClassでもMyClassでも,結果は同じく“MyClassです”と表示されます(図1)。前回までの説明通り,NewClassは親のクラスであるMyClassの機能を引き継いでいるので,ShowMyNameメソッドはNewClassで定義しなくても呼び出すことができます。

図1●受け継いだメソッドは親と子どちらの変数を通しても呼び出せる
図1●受け継いだメソッドは親と子どちらの変数を通しても呼び出せる

 さて,ここからが本題です。NewClassに“NewClassです”というメッセージを表示するメソッドを追加してみます(リスト3)。こうしておいて,リスト2(b)およびリスト2(c)のコードを実行してみましょう(図2)。

class NewClass : MyClass
{
 public void ShowMyName()
 {
  Console.WriteLine("NewClassです");
 }
  …(省略)…
}
リスト3●子クラスに同名のメソッドShowMyNameを追加する
図2●呼び出されるメソッドは変数の型によって決定される
図2●呼び出されるメソッドは変数の型によって決定される