ByVal or ByRef – what’s the difference?

This is a question I see increasingly frequently in forums for some reason, so I thought I’d cobble together a brief synopsis.

ByRef passes a pointer to the variable, so any changes are reflected everywhere that variable is used.

ByVal passes a copy of a variable to a routine, so any changes to that variable will not be reflected in the original routine. In the case of objects, they are always passed as references (pointers) but if you pass an object ByVal you pass a copy of the pointer to the object rather than the original pointer (note: not a copy of the object)

Note: Array variables are always passed ByRef.

(This does not apply to Variants that happen to contain arrays, only to variables that are actually declared as an array.)

 

So here are some examples that will hopefully serve to illustrate the above:

  1. Using a simple data type:
Sub foo()

Dim i As Long
i = 1
Call Change_ByRef(i)
' i is changed by the Change_ByRef procedure and that change IS reflected here too because it was passed by reference
MsgBox "i is now: " & i
Call Change_ByVal(i)
' i is changed only within the Change_ByVal procedure because a COPY of it was passed
MsgBox "i is still: " & i

End Sub

Sub Change_ByRef(ByRef lInput As Long)
lInput = 14
End Sub

Sub Change_ByVal(ByVal lInput As Long)
lInput = 21
End Sub

2. Using an object variable, showing that when passed ByVal, you cannot change the orginal variable to make it point to a different object:

Sub Object_foo()

Dim r As Range
Set r = Range("A1")
Call Change_object_ByRef(r)
' r is changed to a different cell by the Change_ByRef procedure and
' that change IS reflected here too because it was passed by reference
MsgBox "r is now: " & r.Address
Call Change_object_ByVal(r)
' r is changed to a different cell by the Change_ByVal procedure but
' that change IS NOT reflected here because it was passed by value.
MsgBox "r is still: " & r.Address

End Sub

Sub Change_object_ByRef(ByRef rInput As Range)
' change the range variable to one row down
Set rInput = rInput.Offset(1)
End Sub

Sub Change_object_ByVal(ByVal rInput As Range)
' change the range variable to one row down
Set rInput = rInput.Offset(1)
End Sub

3. Again using an object variable, but this time showing that, even when passing ByVal, because you are still passing a pointer to the original object, you can change the properties of the object (you just can’t change which object the variable refers to):

Sub Object_foo2()

Dim r As Range
Set r = Range("A1")
Call Change_object_property_ByRef(r)
' r is given a different value by the Change_ByRef procedure and
' that change IS reflected here because an object reference is passed
MsgBox "r is now: " & r.Value
Call Change_object_property_ByVal(r)
' r is given a different value by the Change_ByVal procedure and
' that change IS reflected here because an object reference is still passed
MsgBox "r is now: " & r.Value

End Sub

Sub Change_object_property_ByRef(ByRef rInput As Range)
' change its value
rInput.Value = "changed byref"
End Sub

Sub Change_object_property_ByVal(ByVal rInput As Range)
' change its value
rInput.Value = "changed byval"
End Sub

For details of an exception to this rule, see the next post.

2 thoughts on “ByVal or ByRef – what’s the difference?

  1. Pingback: When is ByRef not ByRef? | Excel Matters

Leave a Reply

Your email address will not be published. Required fields are marked *