連載

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

Chapter 01 基本構文の変化

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

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

 引数の値渡し(ByVal)と参照渡し(ByRef)のデフォルト

 プロシージャ(関数)へ引数を渡す方法には、主に「値渡し」と「参照渡し」の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:  2
2:  1
3:  2
リスト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: 1
2: 1
3: 2
リスト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: Form1!
2: Form1
リスト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: Form1!
2: Form1!
リスト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:  2
2:  2
リスト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: 2
2: 1
リスト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: 2
2: 1
リスト1-34 リスト1-33の実行結果

 ソース1行目に書かれた引数のデータ型が、Object型からInteger型に変化していることが分かるだろう。このように、可変長引数のデータ型は何であってもよい。VB 6では、Variant型でなければならない制約があったが、VB.NETにはそのような制約は存在しない。


 INDEX
  [連載] 改訂版 プロフェッショナルVB.NETプログラミング
  Chapter 01 基本構文の変化
    1.型文字と新しいデータ型/整数リテラルと浮動小数点リテラル/文字リテラル
    2.日付リテラルについての注意点/変数の宣言をカンマ区切りで続けた場合の相違/変数のスコープ
  3.引数の値渡し(ByVal)と参照渡し(ByRef)のデフォルト/プロパティの参照渡し/可変長引数の参照と値
    4.省略可能な引数/Nothingの振る舞いの変更/行番号使用時のコロン(:)記号
 
「改訂版 プロフェッショナル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