連載

プロフェッショナルVB.NETプログラミング
―― VB 6プログラマーのためのVB.NET入門 ――

第11回 プロシージャとプロシージャ引数

(株)ピーデー
川俣 晶
2002/06/29

Page1 Page2 Page3

ByValとByRefのデフォルト

 プロシージャ(関数)へ引数を渡す方法には、主に「値渡し」と「参照渡し」の2つの方法がある。VB 6(Visual Basic 6.0)では、それぞれ、ByVal、ByRefというキーワードで表現されてきた。これはVB.NET(Visual Basic .NET)にも継承されており、基本的に同様に機能する。

 しかし、ByVal、ByRefのどちらのキーワードも書かなかった場合のデフォルトについて、VB 6とVB.NETの間には相違が存在する。まず、VB 6で、無指定、ByVal、ByRefの3つの引数を使ったサンプル・プログラムを見てみよう。

  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
3種類の引数の引き渡し方法を使用したVB 6のサンプル・プログラム1

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

 1:  2
 2:  1
 3:  2
サンプル・プログラム1の実行結果

 見てのとおり、結果の2行目は、ByValキーワードの効力により、値渡しが行われた結果である。つまり、コードの3行目の計算の結果は、呼び出し元には何の影響も与えないので、コードの9行目で代入された値がそのまま表示されている。一方、結果の3行目は、ByRefキーワードの効力により、参照渡しが行われた結果である。つまり、コードの4行目の計算の結果は、呼び出し元の変数cにも反映され、計算結果の値が表示されている。

 さて問題は結果1行目だが、これは結果3行目と同じになっている。つまり、ByVal、ByRefのどちらも指定されていない場合は、参照渡し(ByRef)と見なされる。

 では、VB.NETで同じことをやろうとすると、どうなるだろうか? 以下は上のプログラムをを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をVB.NETで書き換えたサンプル・プログラム2

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

 1: 1
 2: 1
 3: 2
サンプル・プログラム2の実行結果

 何が起きたかはソース・コードの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
オブジェクトのプロパティを参照渡しするVB 6のサンプル・プログラム3

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

 1: Form1!
 2: Form1
サンプル・プログラム3の実行結果

 コード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
サンプル・プログラム3をVB.NETで書き換えたサンプル・プログラム4

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

 1: Form1!
 2: Form1!
サンプル・プログラム4の実行結果

 見てのとおり、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
VB 6の可変長引数が参照渡しであることを確認するVB 6のサンプル・プログラム5

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

 1:  2
 2:  2
サンプル・プログラム5の実行結果

 結果の2行目が2であることは、確かにソース2行目で加算した結果が、呼び出し元の変数にも反映されていることを示している。

 では、これとほぼ同等の機能をVB.NETで記述するとどうなるだろうか。実際に書いてみたプログラムは以下のとおりである。

  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
サンプル・プログラム5と同等の機能を記述したVB.NETのサンプル・プログラム6

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

 1: 2
 2: 1
サンプル・プログラム6の実行結果

 見てのとおり、結果が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
可変長引数のデータ型をIntegerにしたVB.NETのサンプル・プログラム7

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

 1: 2
 2: 1
サンプル・プログラム7の実行結果

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


 INDEX
  連載 プロフェッショナルVB.NETプログラミング
  第11回 プロシージャとプロシージャ引数
  1.ByValとByRefのデフォルト
    2.プロシージャ脱出とReturn文
    3.省略可能な引数
 
「プロフェッショナル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メールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間