サーチ…
前書き
ByRef
およびByVal
修飾子は、プロシージャのシグネチャの一部であり、引数がプロシージャに渡される方法を示します。 VBAでは、特に指定されていない限り、 ByRef
が渡されます( ByRef
は、存在しない場合は暗黙的です)。
注意:他の多くのプログラミング言語(VB.NETを含む)では、修飾子が指定されていない場合、パラメータは暗黙的に渡されます。混乱を避けるためにByRef
修飾子を明示的に指定することを検討してください。
備考
配列を渡す
配列は参照渡しする必要があります。このコードはコンパイルされますが、実行時エラー424 "Object Required"が発生します。
Public Sub Test()
DoSomething Array(1, 2, 3)
End Sub
Private Sub DoSomething(ByVal foo As Variant)
foo.Add 42
End Sub
このコードはコンパイルされません:
Private Sub DoSomething(ByVal foo() As Variant) 'ByVal is illegal for arrays
foo.Add 42
End Sub
単純な変数ByRefとByValを渡す
通過ByRef
またはByVal
引数の実際の値が渡されているかどうかを示すCalledProcedure
によってCallingProcedure
、または(いくつかの他の言語のポインタと呼ばれる)の参照が渡されているかどうかをCalledProcedure
。
引数が渡された場合ByRef
、引数のメモリアドレスが渡されCalledProcedure
とすることにより、そのパラメータに何らかの変更CalledProcedure
の値に行われるCallingProcedure
。
引数にByVal
が渡された場合、変数への参照ではなく実際の値がCalledProcedure
渡されます。
簡単な例でこれを明確に説明します:
Sub CalledProcedure(ByRef X As Long, ByVal Y As Long)
X = 321
Y = 654
End Sub
Sub CallingProcedure()
Dim A As Long
Dim B As Long
A = 123
B = 456
Debug.Print "BEFORE CALL => A: " & CStr(A), "B: " & CStr(B)
''Result : BEFORE CALL => A: 123 B: 456
CalledProcedure X:=A, Y:=B
Debug.Print "AFTER CALL = A: " & CStr(A), "B: " & CStr(B)
''Result : AFTER CALL => A: 321 B: 456
End Sub
もう一つの例:
Sub Main()
Dim IntVarByVal As Integer
Dim IntVarByRef As Integer
IntVarByVal = 5
IntVarByRef = 10
SubChangeArguments IntVarByVal, IntVarByRef '5 goes in as a "copy". 10 goes in as a reference
Debug.Print "IntVarByVal: " & IntVarByVal 'prints 5 (no change made by SubChangeArguments)
Debug.Print "IntVarByRef: " & IntVarByRef 'prints 99 (the variable was changed in SubChangeArguments)
End Sub
Sub SubChangeArguments(ByVal ParameterByVal As Integer, ByRef ParameterByRef As Integer)
ParameterByVal = ParameterByVal + 2 ' 5 + 2 = 7 (changed only inside this Sub)
ParameterByRef = ParameterByRef + 89 ' 10 + 89 = 99 (changes the IntVarByRef itself - in the Main Sub)
End Sub
ByRef
デフォルト修飾子
パラメーターに修飾子が指定されていない場合、そのパラメーターは暗黙的に参照渡しされます。
Public Sub DoSomething1(foo As Long)
End Sub
Public Sub DoSomething2(ByRef foo As Long)
End Sub
foo
パラメータは、 DoSomething1
とDoSomething2
両方でByRef
に渡されます。
気を付けて!あなたが他の言語の経験を持つVBAに来ているなら、これはあなたが慣れ親しんだものとまったく逆の挙動をする可能性が非常に高いです。他の多くのプログラミング言語(VB.NETを含む)では、implicit / default修飾子は値によってパラメータを渡します。
参照渡し
値が
ByRef
渡すと、プロシージャはその値への参照を受け取ります。Public Sub Test() Dim foo As Long foo = 42 DoSomething foo Debug.Print foo End Sub Private Sub DoSomething(ByRef foo As Long) foo = foo * 2 End Sub
上記の
Test
プロシージャーを呼び出すと、84が出力されますDoSomething
はfoo
が与えられ、その値への参照を受け取り、呼び出し元と同じメモリーアドレスで動作します。ByRef
参照渡しされると、プロシージャはポインタへの参照を受け取ります。Public Sub Test() Dim foo As Collection Set foo = New Collection DoSomething foo Debug.Print foo.Count End Sub Private Sub DoSomething(ByRef foo As Collection) foo.Add 42 Set foo = Nothing End Sub
DoSomething
にオブジェクトポインタへの参照が渡され、返される前にNothing
に割り当てられているため、呼び出し元が存在しなくなったオブジェクトのCount
メンバを呼び出しているため、上記のコードでは実行時エラー91が発生します 。
呼び出しサイトでByValを強制する
コールサイトでカッコを使用すると、 ByRef
をオーバーライドして、引数にByVal
を強制的に渡すことができます。
Public Sub Test()
Dim foo As Long
foo = 42
DoSomething (foo)
Debug.Print foo
End Sub
Private Sub DoSomething(ByRef foo As Long)
foo = foo * 2
End Sub
上記のコードは、 ByRef
が暗黙的に指定されているか明示的に指定されているかにかかわらず、42を出力します。
気を付けて!このため、プロシージャー呼び出しで余分なカッコを使用すると、簡単にバグが発生する可能性があります。手続き名と引数リストの間の空白に注意してください:
bar = DoSomething(foo) 'function call, no whitespace; parens are part of args list DoSomething (foo) 'procedure call, notice whitespace; parens are NOT part of args list DoSomething foo 'procedure call does not force the foo parameter to be ByVal
ByVal
値渡し
値が
ByVal
渡さByVal
と、プロシージャは値のコピーを受け取ります。Public Sub Test() Dim foo As Long foo = 42 DoSomething foo Debug.Print foo End Sub Private Sub DoSomething(ByVal foo As Long) foo = foo * 2 End Sub
上記の
Test
プロシージャーを呼び出すと、42が出力されますDoSomething
はfoo
が与えられ、値のコピーを受け取ります。コピーは2倍され、プロシージャが終了すると破棄されます。発信者のコピーは決して変更されませんでした。参照が
ByVal
渡さByVal
と、プロシージャはポインタのコピーを受け取ります。Public Sub Test() Dim foo As Collection Set foo = New Collection DoSomething foo Debug.Print foo.Count End Sub Private Sub DoSomething(ByVal foo As Collection) foo.Add 42 Set foo = Nothing End Sub
上記の
Test
プロシージャを呼び出すと、1が出力されますDoSomething
はfoo
が与えられ、Collection
オブジェクトへのポインタの コピーを受け取ります。Test
スコープ内のfoo
オブジェクト変数が同じオブジェクトを指しているため、DoSomething
に項目を追加すると、その項目が同じオブジェクトに追加されます。これはポインタのコピーであるため、参照をNothing
設定してNothing
呼び出し元のコピーには影響しません。