|
|
連載
改訂版
プロフェッショナルVB.NETプログラミング
Chapter 01 基本構文の変化
株式会社ピーデー
川俣 晶
2004/02/19 |
|
|
プロシージャ(関数)へ引数を渡す方法には、主に「値渡し」と「参照渡し」の2つの方法がある。VB 6では、それぞれ、ByVal、ByRefというキーワードで表現されてきた。これはVB.NETにも継承されており、基本的に同様に機能する。
しかし、ByVal、ByRefのどちらのキーワードも書かなかった場合のデフォルトについて、VB 6とVB.NETの間には相違が存在する。まず、無指定、ByVal、ByRefの3つの引数を使ったVB 6のサンプル・プログラムを見てみよう(リスト1-21)。
1: Private Sub test(a As Integer, ByVal b As Integer, ByRef c As Integer)
2: a = a + 1
3: b = b + 1
4: c = c + 1
5: End Sub
6: Private Sub Form_Load()
7: Dim a As Integer, b As Integer, c As Integer
8: a = 1
9: b = 1
10: c = 1
11: test a, b, c
12: Debug.Print a
13: Debug.Print b
14: Debug.Print c
15: End Sub
|
|
リスト1-21 3種類の引数の引き渡し方法を使用したプログラム
|
これを実行すると以下のようになる。
|
リスト1-22 リスト1-21の実行結果
|
実行結果の2行目は、ByValキーワードの効力により、値渡しが行われた結果である。つまり、ソースの3行目の計算の結果は、呼び出し元には何の影響も与えないので、ソースの9行目で代入された値がそのまま表示されている。一方、結果の3行目は、ByRefキーワードの効力により、参照渡しが行われた結果である。つまり、ソースの4行目の計算の結果は、呼び出し元の変数cにも反映され、計算結果の値が表示されている。
さて、問題は結果の1行目だが、これは結果の3行目と同じになっている。つまり、VB 6では、ByVal、ByRefのどちらも指定されていない場合は、参照渡し(ByRef)と見なされる。
では、VB.NETで同じことをやろうとすると、どうなるだろうか? 以下はリスト1-21をVB.NETに持っていった例である。
1: Private Sub test(ByVal a As Integer, ByVal b As Integer, ByRef c As Integer)
2: a = a + 1
3: b = b + 1
4: c = c + 1
5: End Sub
6:
7: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
8: Dim a As Integer, b As Integer, c As Integer
9: a = 1
10: b = 1
11: c = 1
12: test(a, b, c)
13: Trace.WriteLine(a)
14: Trace.WriteLine(b)
15: Trace.WriteLine(c)
16: End Sub
|
|
リスト1-23 リスト1-21をVB.NETで書き換えたプログラム
|
これを実行すると以下のようになる。
|
リスト1-24 リスト1-23の実行結果
|
何が起きたかはソース・コードの1行目を見れば明らかだろう。VB.NETでは、ByValもByRefも書かれていない引数に対して、自動的にByValを補う。その結果、ByValもByRefも指定されていない引数というものはなくなってしまう。VB.NETの言語仕様からも、デフォルトはByValであるとされている。そのため、何も考えずにVB 6のソースをVB.NETに持っていくと、ByRef相当で機能していた引数が、ByValとされてしまうケースが生じることになる。例えば、値渡しのときだけ意識的にByValを付け、参照渡しのときは何も付けないようなプログラミングを行ってきたプログラマーは要注意である。
通常、参照渡しは変数を指定して行う。しかし、VB 6の参照渡しは非常に柔軟で、どんな値でも渡すことができる。定数でも計算式でも何でもOKである。その代わり、呼び出し元で、引数が最終的にどのような値になったのかは知ることができない。当然のことながら、何らかのオブジェクトのプロパティも参照渡しに指定することができる。そのことを確認するサンプル・プログラムを以下に示す。
1: Private Sub test(ByRef a As String)
2: a = a & "!"
3: Debug.Print a
4: End Sub
5:
6: Private Sub Form_Load()
7: test Me.Caption
8: Debug.Print Me.Caption
9: End Sub
|
|
リスト1-25 オブジェクトのプロパティを参照渡しするプログラム
|
これを実行すると以下のようになる。
|
リスト1-26 リスト1-25の実行結果
|
ソース7行目の引数でCaptionプロパティを指定している。そして、それをソース2行目で変更している。しかし、ソース8行目で出力する段階では、2行目の変更は反映されていない。プロパティは値として渡っており、参照としては渡っていないからである。
これと同等のプログラムをVB.NETで記述したものを以下に示す。
1: Private Sub test(ByRef a As String)
2: a = a & "!"
3: Trace.WriteLine(a)
4: End Sub
5:
6: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
7: test(Me.Text)
8: Trace.WriteLine(Me.Text)
9: End Sub
|
|
リスト1-27 リスト1-25をVB.NETで書き換えたプログラム
|
これを実行すると以下のようになる。
|
リスト1-28 リスト1-27の実行結果
|
見てのとおり、VB 6とは異なる結果になっている。これはプロパティも参照として渡っていることを示している。つまり、ソース2行目のコードは、Textプロパティの内容を変更しているということである。
VB 6では、可変長の引数を使用することができる。これは、ParamArrayキーワードを付加したVariant型の配列として引数を宣言することで実現できる。可変長引数は参照渡しとして機能し、値渡しにはできない。以下はVB 6の可変長引数が参照渡しであることを確認するプログラムである。
1: Private Sub test(ParamArray a() As Variant)
2: a(0) = a(0) + 1
3: Debug.Print a(0)
4: End Sub
5:
6: Private Sub Form_Load()
7: Dim a As Integer
8: a = 1
9: test a
10: Debug.Print a
11: End Sub
|
|
リスト1-29 VB 6の可変長引数が参照渡しであることを確認するプログラム
|
これを実行すると以下のようになる。
|
リスト1-30 リスト1-29の実行結果
|
結果の2行目が2であることは、確かにソース2行目で加算した結果が、呼び出し元の変数にも反映されていることを示している。
では、これとほぼ同等の機能をVB.NETで記述するとどうなるだろうか。実際に書いてみたプログラムをリスト1-31に示す。
1: Private Sub test(ByVal ParamArray a() As Object)
2: a(0) = a(0) + 1
3: Trace.WriteLine(a(0))
4: End Sub
5:
6: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
7: Dim a As Integer
8: a = 1
9: test(a)
10: Trace.WriteLine(a)
11: End Sub
|
|
リスト1-31 リスト1-29と同等の機能をVB.NETで記述したもの
|
これを実行すると以下のようになる。
|
リスト1-32 リスト1-31の実行結果
|
見てのとおり、結果がVB 6と一致しない。その理由は、ソース・コードの1行目を見れば明らかだろう。ByValキーワードが付いていることから分かるとおり、これは値渡しである。VB.NETでは可変長引数は値渡しになり、参照渡しにはできない。可変長引数を使用したVB 6のソース・コードをVB.NETに移行する際には、参照渡しとしての機能に依存した使い方かどうか、よく確認する必要がある。
余談だが、このプログラムは、以下のように書いても正常に動作する。
1: Private Sub test(ByVal ParamArray a() As Integer)
2: a(0) = a(0) + 1
3: Trace.WriteLine(a(0))
4: End Sub
5:
6: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
7: Dim a As Integer
8: a = 1
9: test(a)
10: Trace.WriteLine(a)
11: End Sub
|
|
リスト1-33 可変長引数のデータ型をIntegerにしたプログラム
|
これを実行すると次のようになる。
|
リスト1-34 リスト1-33の実行結果
|
ソース1行目に書かれた引数のデータ型が、Object型からInteger型に変化していることが分かるだろう。このように、可変長引数のデータ型は何であってもよい。VB 6では、Variant型でなければならない制約があったが、VB.NETにはそのような制約は存在しない。