連載

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

Chapter 05 オブジェクト関連の変化

株式会社ピーデー 川俣 晶
2004/04/15
Page1 Page2 Page3 Page4


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

 ここでは、主にオブジェクトに関係するVB 6とVB.NETの相違点についてまとめてある。「VB.NETになって、Visual Basicもようやくオブジェクト指向になるらしいが、VBプログラマーにオブジェクト指向が分かるのだろうか?」と心配する人も少なくないが、実際にはVB 6の段階で、すでにクラス・モジュールを作成することができ、PrivateやPublicといったキーワードでアクセス範囲を制限する機能も用意されていた。オブジェクト指向を実現する上で、足りない主要な機能は継承ぐらいであったといえる。そのため、ここではVB 6にすでにあって、それが変化した機能のみを解説している。継承に関する機能は、継承とポリモーフィズムで解説している。

オブジェクト生成と解放のタイミングの違い

 リスト5-1はVB 6でクラスを作成して、そのインスタンスを利用する簡単なサンプル・プログラムである。まずは、クラスそのものの定義を示す。「Class1」という名前で、クラスのプロパティ設定は作成したデフォルトのままである。

 1: Private Sub Class_Initialize()
 2:   Debug.Print "Class_Initialize called"
 3: End Sub
 4:
 5: Private Sub Class_Terminate()
 6:   Debug.Print "Class_Terminate called"
 7: End Sub
 8:
 9: Public Sub Test()
10:   Debug.Print "Test called"
11: End Sub
リスト5-1 クラス「Class1」を定義したプログラム

 さて、このクラスのインスタンスを生成して利用するソース・コードとして、リスト5-2のようなものを書いてみた。

1: Private Sub Form_Load()
2:   Dim obj As New Class1
3:   Debug.Print "Dim obj As New Class1"
4:   obj.Test
5: End Sub
リスト5-2 リスト5-1のクラスを利用するプログラム

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

1: Dim obj As New Class1
2: Class_Initialize called
3: Test called
4: Class_Terminate called
リスト5-3 リスト5-2の実行結果

 このサンプル・ソースの動作を説明しておこう。VB 6では、As Newキーワードで宣言されたオブジェクト変数は、インスタンスが自動的に作成されて変数に格納される。しかし、生成されるタイミングは、宣言された行の位置ではなく、実際にそれが使用されたときまで遅延される。そのため、このサンプル・プログラムでは、4行目のobj.Testのコードが最初にこの変数を利用するタイミングになるので、これに先だってインスタンスが生成される。その結果、3行目の“Debug.Print "Dim obj As New Class1"”よりもあとでインスタンスが生成されることになるので、出力結果は、

Dim obj As New Class1

が先で、

Class_Initialize called

があとになる。

 さて、これに相当するコードをVB.NETで記述してみよう。まずはクラス・モジュールから。

 1: Public Class Class1
 2:   Public Sub New()
 3:     Trace.WriteLine("Class1.New called")
 4:   End Sub
 5:
 6:   Protected Overrides Sub Finalize()
 7:     MyBase.Finalize()
 8:     Trace.WriteLine("Class1.Finalize called")
 9:   End Sub
10:
11:   Public Sub Test()
12:     Trace.WriteLine("Test called")
13:   End Sub
14: End Class
リスト5-4 クラスを定義しているVB.NETのプログラム

 これを見ると、VB 6の“Class_Initialize”がNewに、“Class_Terminate”がFinalizeにそれぞれ変わっていることが分かるだろう。なお、6行目のOverridesキーワードについては、Overridesキーワードを使って継承時にメソッドの動作を入れ替えるを、Protectedキーワードについては、Protectedアクセスを参照してほしい。また、MyBase.Finalize()は基底クラスのデストラクタ(Finalizeメソッド)を呼び出している。これらは、継承されたクラスを解放するときに正しく終了処理を行うために必要なものである(継承については、継承とポリモーフィズムを参照)。

 さて、このクラスを呼び出すソース・コードはリスト5-5のようになる。

1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2:   Dim obj As New Class1()
3:   Trace.WriteLine("Dim obj As New Class1()")
4:   obj.Test()
5: End Sub
リスト5-5 リスト5-4のクラスを利用するプログラム

 こちらの方は、2行目の最後に括弧が増えている以外は、ほとんど同じといえるだろう。括弧が付くようになったのは、次で説明するコンストラクタへの引数のためである。

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

1: Class1.New called
2: Dim obj As New Class1()
3: Test called
リスト5-6 リスト5-5の実行結果

 見てのとおり、クラス(リスト5-4)の8行目のコードである“Class1.Finalize called”のメッセージが表示されていない。このメッセージはだいぶあとで見ることができる。タイミングは決まっていないが、筆者が試したときは、ウィンドウを閉じる際に、やっと、以下のメッセージが出力された。

4: Class1.Finalize called
リスト5-7 リスト5-5の実行結果の続き

 相違の原因は、具体的に2つの理由に分けられる。第1の理由は、As Newキーワードを付けたオブジェクト変数でインスタンスを生成するとき、生成するタイミングが遅延されなくなったことにある。このため、表示結果の1行目と2行目が入れ替わってしまっている。これは、タイミングが変わるというだけでなく、VB 6では生成されなかったインスタンスが、VB.NETでは生成される可能性があることに注意しよう。つまり、VB 6では、As Newキーワードを付けたオブジェクト変数を宣言しても、そのオブジェクト変数が使われなければインスタンスは生成されないまま終わったが、VB.NETでは必ず生成されてしまうのである。インスタンスの生成や消滅時に何かの動作を行っているクラスなら、プログラムの動作そのものが変わる可能性があり得る。

 第2の理由は、インスタンスを消滅させるメカニズムが変化したことにある。VB 6では、あるインスタンスへの参照がなくなると、その時点でインスタンスは消滅させられる。しかし、VB.NETでは、.NET Frameworkのほかの言語と同様に、不要となったインスタンスはガベージ・コレクタと呼ばれる自動ゴミ掃除機能が回収する。そのため、VB.NETではデストラクタが実行されるタイミングはガベージ・コレクタ次第であり、プログラマーからは予測できない。つまり、使い終わってから、実際に回収されるまでにタイムラグがあり、その長さがどれぐらいになるかは、特に決まった規則がないことを意味する。

 このようなタイミングの違いは、ときとして、プログラムの正常な動作を阻害するので、VB 6のソースをVB.NET用に書き換えるときは、挙動の違いを正しく把握するようにしておこう。

 明示的に資源を解放するタイミングをコントロールする場合は、例外処理のFinallyブロックやDisposeパターンを使用する。

オーバーロード

 VB 6では、同じ名前のメソッドを同じコンテキスト内で複数記述するとエラーが起きた。リスト5-8はVB 6でエラーになるサンプル・プログラムである。引数の型や数が異なるTwoTimesというメソッドが3つ定義されているが、同じフォーム・モジュール中に同じ名前のメソッドが複数あるとエラーになる。

 1: Private Sub TwoTimes(ByVal n As Integer)
 2:   Debug.Print n * 2
 3: End Sub
 4:
 5: Private Sub TwoTimes(ByVal n1 As Integer, ByVal n2 As Integer)
 6:   Debug.Print (n1 + n2) * 2
 7: End Sub
 8:
 9: Private Sub TwoTimes(ByVal s As String)
10:   Debug.Print s & s
11: End Sub
12:
13: Private Sub Form_Load()
14:   TwoTimes 123
15:   TwoTimes 123, 456
16:   TwoTimes "123"
17: End Sub
リスト5-8 引数の型や数が異なる同名のメソッドを定義したプログラム(コンパイルエラーとなる)

 しかし、これをそのままVB.NET用に書き換えると、これは動作する(リスト5-9)。

 1: Private Sub TwoTimes(ByVal n As Integer)
 2:   Trace.WriteLine(n * 2)
 3: End Sub
 4:
 5: Private Sub TwoTimes(ByVal n1 As Integer, ByVal n2 As Integer)
 6:   Trace.WriteLine((n1 + n2) * 2)
 7: End Sub
 8:
 9: Private Sub TwoTimes(ByVal s As String)
10:   Trace.WriteLine(s & s)
11: End Sub
12:
13: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
14:   TwoTimes(123)
15:   TwoTimes(123, 456)
16:   TwoTimes("123")
17: End Sub
リスト5-9 リスト5-8をVB.NET用に書き換えたプログラム

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

1: 246
2: 1158
3: 123123
リスト5-10 リスト5-9の実行結果

 このように、引数の型や種類が異なる同じ名前のメソッドを宣言することをオーバーロードという。VB.NETでは、オーバーロードが可能となっている。VB 6ではデータは何でもとりあえずVariant型で受けて処理するという方法が使えたので、オーバーロードの必要性は薄かったかもしれない。だが、VB.NETでデータ型を厳密に扱うプログラミング(Option Strictを行うようになると、必須となる機能といえる。同じ名前のメソッドでも、データ型を細かく分けて別々のメソッドとして処理するプログラミングをするようになれば、避けては通れない機能である。

 なお、以下の要素が異なっていても、異なるメソッドとは認識されない。

  • 型メンバの修飾子(SharedやPrivateなど)
  • パラメータの修飾子(ByValやByRefなど)
  • パラメータの名前
  • メソッドの戻り値の型
  • プロパティの要素型

 オーバーロードは、メソッドだけでなく、コンストラクタやプロパティでも使用できる。

コンストラクタへの引数

 インスタンス内部の初期値を、インスタンスを生成する側から設定するケースを考えてみよう。例えば、名前と年齢を保持する2つの変数を持つクラス「Class1」のインスタンスを生成して利用する場合は、この2つの変数に値を設定する必要がある。VB 6では、インスタンスを生成してから、必要な値を設定するので、個別の値を設定する手段をクラスの中に用意しなければならない。リスト5-11は、プロパティ経由で値を設定する機能を含めて作成してみたクラスである。

 1: Private name0 As String
 2: Private age0 As Integer
 3:
 4: Public Property Let name(ByVal s As String)
 5:   name0 = s
 6: End Property
 7:
 8: Public Property Let age(ByVal a As Integer)
 9:   age0 = a
10: End Property
11:
12: Public Sub OutputData()
13:   Debug.Print name0
14:   Debug.Print age0
15: End Sub
リスト5-11 プロパティ経由でクラス内の変数を設定できるプログラム

 これを利用する側のコードは、以下のような感じになる。

1: Private Sub Form_Load()
2:   Dim taro As New Class1
3:   taro.name = "Taro"
4:   taro.age = 17
5:   taro.OutputData
6: End Sub
リスト5-12 リスト5-11を利用するプログラム

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

1: Taro
2:  17
リスト5-13 リスト5-12の実行結果

 これをVB.NET用として書き換えてみよう。クラス内の変数への値の設定は1回限りとすれば、コンストラクタの引数を経由して値を入れてしまうことができる。そのようなクラスを記述してみたのが、リスト5-14である。

 1: Public Class Class1
 2:   Private name As String
 3:   Private age As Integer
 4:
 5:   Public Sub OutputData()
 6:     Trace.WriteLine(name)
 7:     Trace.WriteLine(age)
 8:   End Sub
 9:
10:   Public Sub New(ByVal n As String, ByVal a As Integer)
11:     name = n
12:     age = a
13:   End Sub
14: End Class
リスト5-14 コンストラクタの引数によりクラス内の変数を設定するVB.NETのプログラム

 このソースのNewメソッドに引数が付いていることが分かるだろう。この引数は以下のように記述することで利用できる。

1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2:   Dim taro As New Class1("Taro", 17)
3:   taro.OutputData()
4: End Sub
リスト5-15 リスト5-14を利用するプログラム

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

1: Taro
2: 17
リスト5-16 リスト5-15の実行結果

 このソース(リスト5-15)の2行目のように、As Newキーワードを使用した宣言のときに、クラス名の後に引数を記述することができる。

 コンストラクタに引数を渡すのは、以下のような書式でも可能である。

1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2:   Dim taro As Class1
3:   taro = New Class1("Taro", 17)
4:   taro.OutputData()
5: End Sub
リスト5-17 コンストラクタ呼び出しを別の書式に変えたプログラム
 

 INDEX
  [連載] 改訂版 プロフェッショナルVB.NETプログラミング
  Chapter 05 オブジェクト関連の変化
  1.オブジェクト生成と解放のタイミングの違い/オーバーロード/コンストラクタへの引数
    2.インターフェイス機能の変更点/プロパティ構文の変更/インスタンスを作成せずに呼び出せるメソッド
    3.共有コンストラクタ/既定のプロパティ
    4.引数を持つ既定のプロパティ/Disposeパターン/Instancingプロパティの変更
 
「改訂版 プロフェッショナル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