連載

改訂版
プロフェッショナルVB.NETプログラミング

Chapter 10 継承とポリモーフィズム

株式会社ピーデー 川俣 晶
2004/06/24


 本記事は、(株)技術評論社が発行する書籍『VB6プログラマーのための入門 Visual Basic .NET 独習講座』の一部分を許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。

Windows FormにおけるInheritsステートメントの効果

 VB 6では、以下のようなコードを記述することができる。

1: Private Sub Form_Load()
2:   Debug.Print Width
3:   Debug.Print Height
4: End Sub
リスト10-9 フォームの大きさを表示するプログラム

 これを実行すると以下のようになる。

1:  4800
2:  3600
リスト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とは座標系の初期値が違うため、かなり違う値になっているが、機能的には等価である。

1: 300
2: 300
リスト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キーワードを使って継承時にメソッドの動作を入れ替える

 継承時にメソッドの動作を入れ替える手法には、継承時にメソッドの動作を入れ替えるで説明した以外に、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キーワードを付けるだけなので、継承を意識していないクラスを継承する場合にも容易に対応できる。


 INDEX
  [連載] 改訂版 プロフェッショナルVB.NETプログラミング
  Chapter 10 継承とポリモーフィズム
    1.Inheritsステートメントによる継承機能/クラス内で継承元クラスのメソッドを呼ぶ
  2.Windows FormにおけるInheritsステートメントの効果/継承時にメソッドの動作を入れ替える/Overridesキーワードを使って継承時にメソッドの動作を入れ替える
    3.中身のないメソッドに対し、継承により中身を入れる/Protectedアクセス/継承を禁止するNotInheritableキーワード
    4.ポリモーフィズム(異なる機能と共通の呼び出し方法)
    5.ポリモーフィズム(ソース・コードの弱点を分析/ポリモーフィズムを用いた書き換え)
    6.クラス・ライブラリに見るポリモーフィズムの実例 - その1/その2
 
「改訂版 プロフェッショナルVB.NETプログラミング 」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH