|
|
連載
改訂版
プロフェッショナルVB.NETプログラミング
Chapter 10 継承とポリモーフィズム
株式会社ピーデー
川俣 晶
2004/06/24 |
|
|
VB 6では、以下のようなコードを記述することができる。
1: Private Sub Form_Load()
2: Debug.Print Width
3: Debug.Print Height
4: End Sub
|
|
リスト10-9 フォームの大きさを表示するプログラム
|
これを実行すると以下のようになる。
|
リスト10-10 リスト10-9の実行結果
|
このコードの中のWidthやHeightというキーワードは、フォームのプロパティである。これらは、フォームのインスタンスを明示することなく記述できる。例えば、Me.Widthのように、フォームを明示しなくても利用できる。
これと同じようなコードをVB.NETでも記述することができる。実際に記述してみた例が、リスト10-11である。
1: Public Class Form1
2: Inherits System.Windows.Forms.Form
3:
4: …Windows フォーム デザイナで生成されたコード…
5:
6: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
7: Trace.WriteLine(Width)
8: Trace.WriteLine(Height)
9: End Sub
10: End Class
|
|
リスト10-11 フォームの大きさを表示するVB.NETのプログラム
|
これを実行すると以下のようになる。なお、フォームのサイズが違えば結果も違う数値になる。VB 6とは座標系の初期値が違うため、かなり違う値になっているが、機能的には等価である。
|
リスト10-12 リスト10-11の実行結果
|
このコードの7行目のWidthは、2行目のInheritsで指定された、System.Windows.Forms.Formという名前のクラスが持つプロパティである。このクラスを継承しているクラスForm1の内部なので、これはインスタンスを明示することなく呼び出せるわけである。HeightもWidthと同じ仕組みである。
一見すると、このコードはVB 6の場合とよく似ているが、VB.NETでは継承というメカニズムを通じてプロパティが利用可能になっているという点で、微妙に異なっている点に注意が必要である。この事例では、System.Windows.Forms.Formというクラスを継承しているから、インスタンスを明示しないWidthは、このクラスのプロパティを参照する。しかし、ほかのクラスを継承したクラス内では、同じWidthというキーワードがまったく別のクラスのプロパティを指し示すかもしれない。
メソッドAを含むクラスAを継承して、クラスBを作るときに、メソッドAの内容を入れ替えたい場合がある。つまり、継承を単なる機能追加だけでなく、機能変更にも使用したいということである。もちろん、すべての機能を入れ替えてしまうのなら、継承する必要などない。このような行為は、クラスに含まれる機能の一部を入れ替えたい場合に役に立つことが多い。
まず、次のようなクラスがあったとしよう(リスト10-13)。
1: Public Class Class1
2: Public Sub Test()
3: Trace.WriteLine("Test in Class1 called")
4: End Sub
5: End Class
|
|
リスト10-13 クラスを定義しているプログラム
|
次に、このクラスを継承したときに、Testメソッドに異なる動作をさせたいとしよう。実際にそれを記述したものが、リスト10-14である。
1: Public Class Class2
2: Inherits Class1
3: Public Shadows Sub Test()
4: Trace.WriteLine("Test in Class2 called")
5: End Sub
6: End Class
|
|
リスト10-14 Shadowsキーワードを使用してメソッドを置き換えるプログラム
|
このコードで注目すべき点は2つある。1つは、Class1とまったく同じ名前、同じ引数(つまり引数がないことが共通)というメソッドが、Class2にも存在すること。そしてもう1つは、3行目のSubキーワードの前にShadowsキーワードが記述されていることである。このShadowsキーワードが、継承元クラスにあるメソッドを隠し、このメソッドを有効にする。
これを呼び出すサンプル・プログラムを、以下のように書いてみた(リスト10-15)。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim o1 As New Class1()
3: Dim o2 As New Class2()
4: o1.Test()
5: o2.Test()
6: Dim o3 As Class1
7: o3 = o2
8: o3.Test()
9: End Sub
|
|
リスト10-15 リスト10-14を使用したプログラム
|
これを実行すると以下のようになる。
1: Test in Class1 called
2: Test in Class2 called
3: Test in Class1 called
|
|
リスト10-16 リスト10-15の実行結果
|
4行目のo1.Test()のように、Class1のインスタンスから呼び出せば、もちろん、Class1のメソッドが実行される。また、5行目のo2.Test()のように、Class2のインスタンスから呼び出せば、Class2のメソッドが実行される。
しかし、7行目のように、Class2のインスタンスなのにClass1の変数に代入してからアクセスすると、Class1のメソッドが実行されてしまう。これは、変数の型(ここではClass1)が、呼び出すメソッドを決定しているためである。変数の型に関係なく、置き換えたメソッドを呼び出したい場合は、次に説明するOverridableキーワードを使う。
継承時にメソッドの動作を入れ替える手法には、継承時にメソッドの動作を入れ替えるで説明した以外に、Overridesキーワードを使用する方法がある。
まず、継承元クラスを以下のように変更する。
1: Public Class Class1
2: Public Overridable Sub Test()
3: Trace.WriteLine("Test in Class1 called")
4: End Sub
5: End Class
|
|
リスト10-17 Overridableキーワードを使用してメソッドを定義したプログラム
|
2行目のSubキーワードの前に、Overridableキーワードを挿入する。次に、継承するクラスを以下のように修正する。
1: Public Class Class2
2: Inherits Class1
3: Public Overrides Sub Test()
4: Trace.WriteLine("Test in Class2 called")
5: End Sub
6: End Class
|
|
リスト10-18 Overridesキーワードを使用してメソッドを置き換えるプログラム
|
このように、3行目のSubキーワードの手前のキーワードを、Shadowsではなく、Overridesキーワードに入れ替える。これだけである。では、リスト10-19のようなコードで実際に動作を見るとどうなるだろうか。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim o1 As New Class1()
3: Dim o2 As New Class2()
4: o1.Test()
5: o2.Test()
6: Dim o3 As Class1
7: o3 = o2
8: o3.Test()
9: End Sub
|
|
リスト10-19 リスト10-18を使用するプログラム
|
これを実行すると以下のようになる。
1: Test in Class1 called
2: Test in Class2 called
3: Test in Class2 called
|
|
リスト10-20 リスト10-19の実行結果
|
継承時にメソッドの動作を入れ替えるにあるリスト10-16と比べて、結果の3行目が異なっていることが分かると思う。ここでは、7行目の“o3 = o2”でClass1型の変数にClass2型のインスタンスへの参照を代入しているが、8行目のo3.Test()でメソッドを呼び出すとき、間違いなくClass2のメソッドを呼び出している。
Overridesキーワードを使う方が扱いやすいが、反面、処理速度的にはOverridesキーワードを使わない方法が優れる。どちらを使うかは、ケースバイケースで判断されるものだろう。また、Overridesキーワードを使う方法は、置き換える可能性のあるすべてのメソッドに、あらかじめOverridableキーワードを付けておく必要がある。その点で、実際に継承される前から、継承を意識しておく必要があり、あまり手軽ではない。
一方、Overridesキーワードを使わない方法は、継承する際にShadowsキーワードを付けるだけなので、継承を意識していないクラスを継承する場合にも容易に対応できる。