VB.NETには関数への引数渡しが4パターンある。本質的にはByValによる値渡しとByRefによる参照渡しの2パターンだが、この記事では型の違いも考慮して4パターンと考える。
それぞれの違いをまとめておく。
順番に見ていく。
値型の値渡し
Sub FuncValType(ByVal dt As Integer)
dt = 20
End Sub
Sub Main()
Dim data As Integer = 10
Console.WriteLine(data) '10が表示される
FuncValType(data)
Console.WriteLine(data) '10が表示される
End Sub
C言語と同様に実引数の値が仮引数にコピーされるため、関数呼び出し後は呼び出し側の実引数dataは変化しない。

値型の参照渡し
Sub FuncValType(ByRef dt As Integer)
dt = 20
End Sub
Sub Main()
Dim data As Integer = 10
Console.WriteLine(data) '10が表示される
FuncValType(data)
Console.WriteLine(data) '20が表示される
End Sub
C言語風に考えると実引数のメモリ番地が仮引数にコピーされるため、関数呼び出し後は呼び出し側の実引数dataの値が10から20に更新される。

参照型の値渡し
Class Widget
Public Property X As Integer
Public Sub New(ByVal X As Integer)
Me.X = X
End Sub
End Class
Sub FuncRefType(ByVal w As Widget)
w.X = 20 '---- ①
w = New Widget(30) '---- ②
End Sub
Sub Main()
Dim data As Widget = New Widget(10)
Console.WriteLine(data.X) '10が表示される
FuncRefType(data)
Console.WriteLine(data.X) '20が表示される
End Sub
この場合、実引数の値であるメモリ内容はメモリ番地になっており、それが仮引数にコピーされるため、実引数dataと仮引数wは同一のインスタンスを参照することになる。上記コードの①のように仮引数w経由でプロパティXを変更すると、その変更は呼び出し側の実引数dataにも影響が及ぶ。ただし②のように仮引数wに新しいインスタンスを代入すると、wとdataは異なるインスタンスを参照することになる。

参照型の参照渡し
Class Widget
Public Property X As Integer
Public Sub New(ByVal X As Integer)
Me.X = X
End Sub
End Class
Sub FuncRefType(ByRef w As Widget)
w.X = 20 '---- ①
w = New Widget(30) '---- ②
End Sub
Sub Main()
Dim data As Widget = New Widget(10)
Console.WriteLine(data.X) '10が表示される
FuncRefType(data)
Console.WriteLine(data.X) '30が表示される
End Sub
値型の参照渡しと同じくメモリ番地がコピーされるため、実引数dataと仮引数wは同一のインスタンスを参照することになる。そのため上記コードの①のように仮引数w経由でプロパティXを変更すると、その変更は呼び出し側の実引数dataにも影響が及ぶ。ここまでは参照型の値渡しと同じ結果になる。しかし、上記コードの②のように仮引数wに新しく生成されたインスタンスを代入すると、呼び出し側の実引数dataが指し示すインスタンスはwが指し示すインスタンスと同一になる。このとき、もともと実引数dataが参照していたインスタンスはどこからも参照されなくなるため、ガベージコレクション(GC)の対象となる。

値型の参照渡しでも参照型の参照渡しでも、ByRefキーワードを付けて定義した仮引数を実引数のエイリアス(別名)と考えれば特に問題はない。その場合、仮引数の値を書き換えると、仮引数の指し示している先のメモリ内容が書き換えられることを覚えておけばよい。
コメント