サーチ…
前書き
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 SubDoSomethingにオブジェクトポインタへの参照が渡され、返される前に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呼び出し元のコピーには影響しません。