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
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