I think I've recently (finally) started to get to grips with how ByVal
and ByRef
work in VBA.
In all the tutorials and references I've been through to date, ByRef
was always "passing the object", ByVal
was "passing a copy of the object". To me the latter meant the object's place in memory was duplicated and a pointer to that new location was returned. I now realise that's not always the case, in fact as far as I can tell it's rarely the case: instead, most objects and classes are in fact passed ByRef
even with ByVal
specified in a routine's signature.
A System.Collections.ArrayList
is passed silently ByRef as demonstrated in this code:
Sub test()
Dim list1 As Object, list2 As Object
Set list1 = CreateObject("System.Collections.ArrayList")
list1.Add "foo"
Set list2 = RemoveItem(list1)
Debug.Assert list2.Contains("foo") = False 'as expected
Debug.Assert list1.Contains("foo") = True 'raises error, meaning list1 was passed byref not byval
End Sub
Function RemoveItem(ByVal list As Object) As Object 'ByVal
list.Remove "foo" 'expect to remove from a copy and return that
Set RemoveItem = list
End Function
That surprised me given what I thought I knew about ByVal
. Further digging shows me that to get a copy like I want from ByVal
I need the objects I'm passing to have a method that enables this. For an ArrayList
, the .Clone
method makes a shallow copy. So my function becomes:
Function RemoveItem(ByRef list As Object) As Object 'ByRef or ByVal, makes no difference
Dim listCopy As Object
Set listCopy = list.Clone 'make a shallow copy of the object
listCopy.Remove "foo" 'actually remove from a copy and return that
Set RemoveItem = listCopy
End Function
A VB Array
raises a compiler error when passed ByVal, perhaps as a warning against just this
All that got me thinking:
ByVal
and ByRef
for classes/objectsBoolean
, Long
) which can be passed ByVal
from classes and types that can'tVB6
ByVal
response?
ByVal
and return a .Clone
of itself.ByVal
and can any other objects mimic this behaviour (again, in Python, everything's an object so everything's behaviour can be copied*). Or do I not have to worry about this ever happening?Array
's behaviour and raise a compiler error when passed ByVal
- as an array has no clone method and cannot be readily copied so is always ByRef
*anecdotal, I'm very new to Python
In all the tutorials and references I've been through to date, ByRef was always "passing the object", ByVal was "passing a copy of the object".
Do you have a link for an example of the above?
For objects (created from classes) ByVal
is like *foo
whilst ByRef
is like **foo
.
Boolean and Long are primitives, for primitives ByVal
is like bar
whilst ByRef
is like *bar
.
ByRef
or ByVal
.Long
is a 32-bit integer so is Boolean
in fact but can only take one of two values (0=False,-1=True). You need not worry, VBA places limits in the name of safety.ByRef
for your objects then create a Type instead of a class.For classes, you will have to write your own copy constructors both shallow and deep. But Types can be copied just by using =
both shallow and deep.
Arrays and Types are passed ByRef
because they are created on the stack.