About ByRef vs ByVal

As I sit here dreading the fate of the doomed Yankees, I find myself pondering the question posed by a friend yesterday about ByRef vs. ByVal. With nothing better to do at the moment, I thought I’d take a stab at explaining it.

When creating a method, you can define the given parameters in two ways: ByVal (the default) or ByRef. What’s the difference?

To properly understand it, step back and realize that the parameters are just variable assignments. If you define a method Sub MyMethod(x As Integer), calling MyMethod(c) has the same effect on the parameter x as x = c.

But this is the important concept behind variables (and properties and parameters): all variables hold a reference to a Thing, and assigning one variable to another assigns the reference to that Thing, never a copy.

Let me say that again. Assigning one variable to another never creates a copy of the Thing you are assigning. This is true if you are assigning an object like a Date or Dictionary, an array, or just a simple String.

Think of it this way. If you build a house, there is only one house no matter how many envelopes you write its address on. You can copy that address 100 times, but you’ll never gain square footage. The variable is the envelope holding that address.

(Yeah, I know that’s an imperfect analogy, but it’s the best I’ve got.)

This behavior is apparent with objects when you assign the Thing in var1 to var2, make a change through var2, and the properties in var1 change too. That’s because the Thing doesn’t live in var1 or var2, but out in the ether, and those variables simply provide a path to it.

(This is less apparent when it comes to String because it looks to us like assigning one String variable to another makes a copy. It doesn’t, but since you can’t change a String, only create a new one, it acts like it’s a copy.)

This brings us full circle to ByRef vs. ByVal. So what’s the difference?

When you use ByVal (the default if you don’t specify either), the behavior is the same as if you assigned var1 to var2. Any changes made to the Thing through var2 are reflected through var1, but when you assign a new Thing to var2, it no longer has a relationship to var1. They each hold references to two different Things.

But when you use ByRef, you aren’t sending a reference to the Thing to your method like you would when assigning one variable to another, you are sending a reference to the variable to your method. This means that any changes made to that parameter within the method are made directly to the variable you used when you called the method.

For example:

MyMethod(orig)

// Different ways of defining MyMethod
Sub MyMethod(x As Date)
  x = new Date // orig is unaffected

Sub MyMethod(ByRef x As Date)
  x = new Date // orig now has a new Date

In short, unless you specify ByRef, all parameters are passed By Value. And no, I don’t care what the docs say, that’s how it works. The badly-named ByRef is passing a reference to the calling variable that holds the Thing instead of a reference to the Thing itself.

Where might you use this? The most common is to return multiple values, something you otherwise cannot do directly. For example:

Sub ParseFilename(n As String, ByRef returnPref As String, ByRef returnSuff As String)
  dim parts() as string = n.Split( “.” )
  returnSuff = n( n.Ubound )
  parts.Remove n.Ubound
  returnPref = join( parts, “.” )
End Sub

dim p as string
dim s as string
ParseFilename( “My.Very.Educated.doc”, p, s )

// p = My.Very.Educated
// s = doc
// (no, I didn’t test this code)

Now that that’s off my chest, I’ll go watch the Yankees make a stunning comeback and not end their season today.

1 Like

A common mistake is thinking that objects carries its contents “ByVal”. But Objects are ALWAYS references.

[code]Class Class1
Public Property aVal as String = “Null”
End Class

Public Sub myRef(ByRef c As Class1)
c.aVal = “ref”
End Sub

Public Sub myVal(ByVal c As Class1)
c.aVal = “val”
End Sub

Sub Run()

Var v As New Class1 // Objects are ALWAYS references

// v.aVal = “Null”

myRef(v)

// v.aVal passed ByRef, becomes “ref”

myVal(v)

// v.aVal passed ByVal, ALSO changes and becomes “val”

End Sub
[/code]

[quote=459187:@Rick Araujo]A common mistake is thinking that objects carries its contents “ByVal”. But Objects are ALWAYS references.
[/quote]

i agree.
its unaesthetic but a assignment with = can be a reference or a copy of the data (integer & strings)
byval for objects is confusing because they are not copied.

ByRef is easy to understand, a change of this variable will change the variable from the calling method.

Please see my post. It’s never a copy.

Any primitive value, passed ByVal is a disposable copy of the pointed original value. Sometimes, optimizer compilers, even uses CPU registers to hold and pass some of those values in a fast way, like a Integer, for example, but the original one still there, untouchable in its memory place.

Not true.

Var x1 As Integer = 1
Var x2 As Integer = x1

Creates 2 instances of the number 1, the second one is a copy of the contents of x1. If you go to the memory location where x1 stores that 1, and change it to 2, and peek the contents of what x2 points to, x2 points to an 1, not that 2.

I suppose that makes sense for numeric and, probably, Boolean.

Any primitive values. Ints, chars, doubles, etc. Usually Objects and Structures are structural values needing references moving around instead of its values.

Things like Dictionaries and Dates internally are objects, so they are also reference types.

In OOP, when needed, we usually implement a method like .Clone() to return a copy of something, instead of a reference of THAT something.

[code]Class Class1

Public Property myVal as Integer

Public Function Clone() as Class1

Var c As New Class1 // make a new one
c.myVal = self.myVal // clone its value
Return c

End Function

End Class

Public Sub Run()
Var v1, v2 As Class1

v1 = New Class1

v1.myVal = 11

v2 = v1 // v2 = same as v1

v2.myVal = 22

MessageBox v1.myVal.ToString // Guess what? 22

v1.myVal = 11

v2 = v1.Clone()

MessageBox v2.myVal.ToString // Guess what? 11

v2.myVal = 22

MessageBox v1.myVal.ToString // Guess what? 11

MessageBox v2.myVal.ToString // Guess what? 22

End Sub
[/code]

A differing point of view, but I look at it as things passed ByVal are always a copy. A copy of the thing you’re passing - namely, a reference. You get a copy of the reference, not a copy of the object being referenced. Now you have 2 distinct and separate references to the same object. But I suppose the end result is the same. It’s all in what cognitive model helps you understand the concept best. Which could be different than mine.

[quote=459181:@Kem Tekinay]As I sit here dreading the fate of the doomed Yankees, I find myself pondering the question posed by a friend yesterday about ByRef vs. ByVal. With nothing better to do at the moment, I thought I’d take a stab at explaining it.

When creating a method, you can define the given parameters in two ways: ByVal (the default) or ByRef. What’s the difference?

To properly understand it, step back and realize that the parameters are just variable assignments. If you define a method Sub MyMethod(x As Integer), calling MyMethod(c) has the same effect on the parameter x as x = c.

But this is the important concept behind variables (and properties and parameters): all variables hold a reference to a Thing, and assigning one variable to another assigns the reference to that Thing, never a copy.

Let me say that again. Assigning one variable to another never creates a copy of the Thing you are assigning. This is true if you are assigning an object like a Date or Dictionary, an array, or just a simple String.
[/quote]
It really depends on whether the variable is a VALUE type or a REFERENCE type not on whether it is passed byref or byval. In fact the handling of byref and byval is the same for each. The trick is whether the type is a VALUE or REFERENCE type.

With a value type when you assign from one value type (integer) to another (integer) you are actually copying the actual numeric value that it holds.
With a reference type this is still true EXCEPT what you are copying is a REFERENCE (basically both references hold the memory location of the thing they represent)

dim a as integer // set aside a new memory spot to hold an integer
dim b as integer // set aside a new memory spot to hold an integer
a = 100 // put the value 100 in the memory spot we call "a"
b = a // COPY the value held in "a" into the spot reserved for "b"

dim d as new Date // allocate a new chunk of memory and put the address of that memory in "d"
dim e as Date // set aside as spot that can hold an address of a date object

e = d // make the address of d be put into e

With a value type, like an INTEGER, if you pass one to a method BYVAL you DO get a copy
A new temporary named whatever the parameters name is is created, the VALUE put into it, and the method can happily use that for the life of the method
When the method ends that parameter is removed from memory and the original remains unaltered.

With a REFERENCE type when you pass it by BYVAL you get a COPY of the original reference (the same as with a value type) that you can use to alter the contents of the variable.
A new temporary variable is created and a copy of the original reference put into it and the method can use this newly named parameter as though it were the original. When the method ends the temporary is deleted from memory and the original reference remains unchanged.

When you pass a VALUE type BYREF, a temporary variable is created, but this time it holds a reference to the original.
And in the method you called when any code alters this temporary it actual alters the original
When the method exits the temporary is removed and the original remains
AND the exact same is true for REFERENCE types like Dates etc

https://blog.xojo.com/2018/12/12/what-kind-of-variable-are-you/
https://blog.xojo.com/2019/01/23/byref-vs-reference-types/
https://blog.xojo.com/2019/01/24/some-follow-up-regarding-byref/

I always thought I understood, but now I am lost.

You’d probably not in need unless you have very special requirements.

BYVAl passes the VALUE into a temporary
BYREF passes a reference to the item

Then the question is what TYPE of thing have you passed byval or byref
IF it is an intrinsic type like integer, double you get a COPY of that value in the temporary inside the method
Changes to this will not be reflected in the original

IF it is a reference type (like one you get from NEW DATE) the what is passed is a copy of the reference (basically a copy of the pointer)
This would still allow you to change the values held IN the date object - but wont allow you to change what the original refers to

IF you BY REF then what you get are REFERENCES
With an VALUE type (usually integers, doubles, uints, booleans colors etc) this lets you change the value in the called method you passed it into BYREF and those changes will be preserved when you go back to the original code
With a REFERENCE TYPE the same is type - your passing a reference in but this time you can actually change what the original referred TO

Try this out

  1. by val with an intrinsic type
    in window1.open / opening put
dim i as integer
foo(i)
break // when you get here look a tthe value of i it will NOT show the value from calling foo

and add a method

sub foo(I_passed_byval as integer)

     I_passed_byval = 32
     break // when you get here look a tthe value of I_passed_byval in the debugger
end sub
  1. by val with a reference type (make sure you have show id’s set ON in the debugger)
    Preferences > Debugging > Show Object ID’s in variable lists
    in window1.open / opening put
dim d as new date
break // when you get here look at the OBJECT ID of d_pass_byval (long string of hex)
foo(d)
// when you get here look at OBJECT ID (value) of d it will NOT show the value from calling foo
// it will be the same one written down BEFORE the call of foo
break    

and add a method

sub foo(d_passed_byval as date)
     break // when you get here look at the OBJECT ID of d_pass_byval (long string of hex)
     d_passed_byval = new date
     break // when you get here look at the value of d_passed_byval in the debugger
end sub

so in case 2 the “value” of D is the object id (basically address) and we cannot change that
we CAN change the data in the things d points to (the date) inside or outside the method
thats just the nature of reference types

now we get to the harder one
3) by REF with an intrinsic type
in window1.open / opening put

dim i as integer
foo(i)
break // when you get here look a tthe value of i it WILL show the value from calling foo

and add a method

sub foo(byref I_passed_byval as integer)

     I_passed_byval = 32
     break // when you get here look at the value of I_passed_byval in the debugger
end sub
  1. by REF with a reference type (make sure you have show id’s set ON in the debugger)
    Preferences > Debugging > Show Object ID’s in variable lists
    in window1.open / opening put
dim d as new date
break // when you get here look at the OBJECT ID of d_pass_byval (long string of hex)
foo(d)
// when you get here look at OBJECT ID (value) of d it WILL show the object ID assigned in FOO
// it will NOT be the same one written down BEFORE the call of foo
break    

and add a method

sub foo(byref d_passed_byval as date)
     break // when you get here look at the OBJECT ID of d_pass_byval (long string of hex)
     d_passed_byval = new date
     break // when you get here look at the value of d_passed_byval in the debugger
end sub

so in case 4 the “value” of D is the object id (basically address) and we CAN change that when we use byref

so in all cases BYVAL gives you a temporary that behaves like a local variable
and in all cases BYREF gives you a references that lets you change the original
the question of “what are you changing” relies on whether it is a VALUE type or a REFERENCE type

It is my understanding that all vectors and arrays are passed by value regardless of whether you use ByRef or ByVal. Is that still true?

[code]// Arrays are passed as references

Public Sub PassArray(arr() As String)

arr(1) = “Changed”

End Sub

Public Sub Run()

Var anArray() As String = Array(“val1”, “val2”)

PassArray(anArray)

// Shows “changed”, so Array is passed as reference, a copy could be huge for a huge array…
MessageBox anArray(1)

End Sub
[/code]

[code]// Strings are primitive types, so, unless requested ByRef,
// String params receive a new disposable value copy, “ByVal”

Public Sub PassString(s As String)
s = “Changed”
End Sub

Public Sub Run()

Var aString As String = “By Value”

PassString(aString)

// Shows “By Value”, so it received a copy. Manipulating huge strings ByVal can be costly
MessageBox aString

End Sub
[/code]

Not exactly. Arrays are REFERENCE types so they operate under the rules described by Norman. If you pass ByVal, you can modify the contents of the array. If you pass ByRef, you can replace the entire array with a brand new one.

Excellent answer and reference (pun intended), thank you!
I rarely use the ByRef keyword, albeit I don’t code “rarely”; it’s just I use other ways. Dealing with these 4 possibilities is always a “deep thought”, where your explanation will be a quick reference; very helpful!

welcome
I try to help
always have

[quote=459330:@Norman Palardy]welcome
I try to help
always have[/quote]
And I appreciate you also for that.