Re: Please help me clarify these byVal vs. byRef subtleties

From: Jay B. Harlow [MVP - Outlook] (Jay_Harlow_MVP_at_msn.com)
Date: 11/11/04


Date: Thu, 11 Nov 2004 16:31:33 -0600

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

"Warren Sirota" <wsirota@wsdesigns.com> wrote in message
news:eeJUW0DyEHA.4060@TK2MSFTNGP10.phx.gbl...
> 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