|
|
連載
プロフェッショナルVB.NETプログラミング
―― VB 6プログラマーのためのVB.NET入門 ――
第9回 オブジェクト関連(前編)
(株)ピーデー
川俣 晶
2002/06/15
|
オブジェクト関連のタイミングの違い
VB.NET(Visual Basic .NET)のことは、VB以外のプログラマーの間でも話題に上ることがある。「VB.NETになって、Visual Basicもようやくオブジェクト指向になるらしいが、VBプログラマーにオブジェクト指向が分かるのだろうか?」と心配する人が少なくないからだ。しかし、言語仕様の面から見れば、VB 6(Visual Basic 6.0)の段階で、すでにクラス・モジュールを作成することができ、PrivateやPublicといったキーワードでアクセス範囲を制限する機能も用意されていた。オブジェクト指向を実現する上で、足りない主要な機能は継承ぐらいである。そして、VB.NETで継承機能がサポートされたことで、Visual Basicを使った本格オブジェクト指向プログラミングの準備が整ったのである。しかし、逆の方向から見れば、継承以外の主要な機能はVB 6にもあったわけで、VBプログラマーがオブジェクト指向に関するすべてに無知であると考えるのは無理がある。そのようなわけで、VB 6からすでに存在したオブジェクト関連の機能について、VB.NETでどう変化したか見てみよう。
以下は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
|
|
クラス“Class1”を定義したVB 6のサンプル・プログラム1 |
さて、このクラスのインスタンスを生成して利用するコードは以下のようなものを書いてみた。
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
|
|
サンプル・プログラム1のクラスを利用するVB 6のサンプル・プログラム2 |
これを実行すると以下のようになる。
1: Dim obj As New Class1
2: Class_Initialize called
3: Test called
4: Class_Terminate called
|
|
サンプル・プログラム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
|
|
クラスを定義しているVB.NETのサンプル・プログラム3 |
これを見ると、VB 6の“Class_Initialize”がNewに、“Class_Terminate”がFinalizeにそれぞれ変わっていることが分かるだろう。6〜9行目については、実は継承の機能と関係するキーワードが大量に含まれていて、継承を理解せずに正しく内容を理解することは困難である。今回は、オブジェクト生成のタイミングをテーマにしているので、理解できなくても構わない。理解している人のために余談として説明すると、Protectedは継承されたクラスからのみアクセスを許すキーワードであり、Overrideは仮想メソッドをオーバーライドすることを意味するキーワードである。MyBase.Finalize()は基底クラスのデストラクタ(Finalizeメソッド)を呼び出している。これらは、継承されたクラスを解放するときに正しく終了処理を行うために必要なものである。
さて、このクラスを呼び出すコードは以下のようになる。
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
|
|
サンプル・プログラム3のクラスを利用するVB.NETのサンプル・プログラム4 |
こちらの方は、2行目の最後に括弧が増えている以外は、ほとんど同じといえるだろう。括弧が付くようになったのは、次で説明するコンストラクタへの引数のためである。
これを実行すると以下のようになる。
1: Class1.New called
2: Dim obj As New Class1()
3: Test called
|
|
サンプル・プログラム4の実行結果 |
見ての通り、クラスの7行目のコードである「Class1.Finalize called」のメッセージが表示されていない。このメッセージはだいぶ後で見ることができる。タイミングは決まっていないが、筆者が試したときは、ウィンドウを閉じるときに、やっと、以下のメッセージが出力された。
4: Class1.Finalize called
|
|
サンプル・プログラム4の実行結果の続き |
相違の原因は、具体的に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用に書き換えるときは、挙動の違いを正しく把握するようにしよう。
Insider.NET 記事ランキング
本日
月間