Please help me clarify these byVal vs. byRef subtleties

W

Warren Sirota

Hi,

Please let me know if I am interpreting this correctly. I've done a
little testing of the difference between passing parameters byVal and
byRef, and the results were slightly non-intuitive, as I expected. I
haven't seen this behavior explained precisely in the .net world yet,
so I wanted to check and make sure I've got it right.

I apologize that this is a bit long. I've tried to keep it concise.
There are code fragments here, but you should be able to scan through
without actually cutting, pasting and running it.

I've got a class, DataHider, with one private string field (i.e.,
private, non-shared module-level variable of type string). DataHider
also has simple public getter and setter methods for the field, which
do nothing other than retrieve or set the values. It's simple because
it's just for testing.

Then I've got a module with two nearly-identical routines, that only
differ by how the parameters are passed to it:

Public Sub callObjectMethodPassedByVal(ByVal myObj As
DataHider)
myObj.setMyData("abcdef")
End Sub

Public Sub callObjectMethodPassedByRef(ByRef myObj As
DataHider)
myObj.setMyData("abcdef")
End Sub

The counterintuitive thing (at least to me) is that *both* of these
routines succeed in changing the data value in myObj, as you can test
with this code fragment if you like:

Private Sub Button2_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Button2.Click
Dim hider As New DataHider
Debug.WriteLine("**** Test 2 ****")
hider.setMyData("fred")
Debug.WriteLine("Initial data is: " & hider.getMyData)
objectByVal(hider)
Debug.WriteLine( _
"Data after changing in a sub called byVal is: " _
& hider.getMyData)
objectByRef(hider)
Debug.WriteLine( _
"Data after changing in a sub called byRef is: " _
& hider.getMyData)

End Sub

This routine ends up printing the following in the output window:
**** Test 2 ****
Initial data is: fred
Data after changing in a sub called byVal is: abcdef
Data after changing in a sub called byRef is: abcdef

*****************
I would have thought that an object passed in byVal couldn't be
changed. Now it seems that it all depends on what you mean by "change"
(and perhaps, what "is" is...) It seems that thinking of byVal as
meaning that the called routine works on a "copy" of the passed in
object is at best misleading.

My explanation (please tell me if this is accurate) is that
When you pass an object by value, you are essentially passing its
*address*, and the address is the part that can't be changed. How is
this different than passing byRef? I think that the difference only
shows up when you try to reassign the passed in parameter to another
object (i.e, issue a "myObj = whatever" statement). As long as you
don't do that, it appears to me that byRef and byVal function more or
less identically.

****************
More tests:

reassign the object before changing the data value:


Public Sub objectByVal3(ByVal myObj As DataHider)
myObj = New DataHider 'This is new
myObj.setMyData("abcdef")
End Sub
Public Sub objectByRef3(ByRef myObj As DataHider)
myObj = New DataHider 'This is new
myObj.setMyData("abcdef")
End Sub

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button3.Click
Dim hider As New DataHider
Debug.WriteLine("**** Test 3 ****")
hider.setMyData("fred")
Debug.WriteLine("Initial data is: " & hider.getMyData)
objectByVal3(hider)
Debug.WriteLine( _
"Data after changing in a sub called byVal is: " _
& hider.getMyData)
objectByRef3(hider)
Debug.WriteLine( _
"Data after changing in a sub called byRef is: " _
& hider.getMyData)
End Sub

**** Test 3 ****
Initial data is: fred
Data after changing in a sub called byVal is: fred
Data after changing in a sub called byRef is: abcdef

I think this evidence tends to confirm my theory.

******************************************************
One more tiny, but fascinating test:

In a single module, declare
private m_hider as DataHider

and the following two routines:

Public Sub objectByVal4(ByVal myObj As DataHider)
Debug.WriteLine(myObj Is m_hider)
End Sub


Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button4.Click
Debug.WriteLine("**** Test 4 ****")
m_hider = New DataHider 'm_hider has to be declared at module
'level
objectByVal4(m_hider)
End Sub


When you call objectByVal4 from the Click event as above, it prints
"True". I find this to be massively counterintuitive, unless you think
of byVal as a copy of the address of the object. In that view, it makes
perfect sense that if you add "myObj = New DataHider" before the
debug.writeline, you will then get a False printout in the output
window. That is, in fact, what occurs.

I'd appreciate any corrections or clarifications to this
conceptualization of the difference between the two passing methods.

Thanks,
Warren
 
J

Jay B. Harlow [MVP - Outlook]

Warren,
ByVal & ByRef Parameters are independent of Reference & Value Types. All
parameters in VB.NET by default are passed ByVal, you should only pass a
parameter ByRef when you have to, which is when you need to modify the
callers variable.

A Reference Type is an object that exists on the heap. If I have a variable
that is a reference type and assign the variable to another variable. Both
variables will be pointing to the same object on the heap.

Dim x As Person
x = New Person()
Dim y As Person
y = x

Both x & y are the exact same Person object on the heap.

A Value Type does not live on the Heap. If I have a value type variable and
I assign it to another variable, a copy of the value is made.

Dim x As Integer
x = 100
Dim y As Integer
y = x

Although both x & y have the value 100, they are physically different values
as a copy was made.

Now when you pass a variable to a ByVal parameter a copy of the variable is
made. So for a Reference Type a copy of the reference is made, which means
there is still only one object on the heap & two references to that object.
For a Value Type a copy of the value is made.

When you pass a variable to a ByRef parameter a reference to that variable
is made. So for a Reference Type you have a reference to a reference to the
object, for a Value Type you have a reference to the value.

Remember ByVal & ByRef are how parameters are passed. Reference & Value
Types are how quantities are stored.

The following site also does a good job of explaining Reference & Value
types & ByRef & ByVal parameters.

http://www.yoda.arachsys.com/csharp/parameters.html


Hope this helps
Jay
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top