サーチ…


前書き

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パラメータは、 DoSomething1DoSomething2両方で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が出力されますDoSomethingfooが与えられ、その値への参照を受け取り、呼び出し元と同じメモリーアドレスで動作します。

  • 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が出力されますDoSomethingfooが与えられ、値のコピーを受け取ります。コピーは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が出力されますDoSomethingfooが与えられ、 Collectionオブジェクトへのポインタの コピーを受け取ります。 Testスコープ内のfooオブジェクト変数が同じオブジェクトを指しているため、 DoSomethingに項目を追加すると、その項目が同じオブジェクトに追加されます。これはポインタのコピーであるため、参照をNothing設定してNothing呼び出し元のコピーには影響しません。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow