Is ByRef vs ByVal generating errors?
ByRef
vs ByVal
generates errors !?
I had a method that used Object
Function Foo(ByRef bar as CustomObject) as Boolean
this method generated errors as some strange .NET Runtime things changed the object bar
by calling Dispose () al.
Lots of time spent figuring out the thing (where the object changes ...) until someone replaced ByRef
with ByVal
and the object changed when going to this method ...
Can anyone explain this what's going on?
Nota Bene (change)
As in my case, Foo
does the function do NOT modifybar
, should it ByRef
or ByVal
have the same effect ?
Foo
just read Properties from bar
.
The code:
Module Module1
Sub Main()
Dim b As New Bar
' see the output bellow '
Foo(b.Name)
Console.ReadLine()
End Sub
Function Foo(ByRef name As String) As Boolean
Console.WriteLine("Name is : '{0}'", name)
End Function
Class Bar
Private _Name As String = "John"
Property Name()
Get
Return _Name
End Get
Set(ByVal value)
If _Name IsNot Nothing Then
'_Name.Dispose() If this were an IDisposable, would have problems here'
End If
Console.WriteLine("Name is Changed to '{0}'", value)
End Set
End Property
End Class
End Module
Output:
Name: "John"
Name changed to "John"
a source to share
In VB.NET, passing a property by reference updates the actual property, not the underlying value. Thus, when Foo exits, the CLR calls the Property Set method to update the property value with any new value at the end of the function (even if it hasn't changed).
This behavior is described in the VB.NET Language Specification (reference parameters, last 3 paragraphs):
http://msdn.microsoft.com/en-us/library/aa711958(v=VS.71).aspx
a source to share
Passing an argument ByRef
means that if someone assigns a new value to a variable, that new value will be passed back to the calling function. Passing ByVal
passes a copy of this value to the function, so changes are not passed back to the caller.
Note that when I refer to the value, this is what is actually stored in this variable. With reference types, this means it is a reference. Passing a reference type by value does not copy the entire instance, it just copies the reference . This means that any changes made to the object itself will still be visible to the calling function.
For example, consider this class:
Public Class Foo
Private m_Value as string
Public Property Value as String
Get
return m_Value
End Get
Set(Value as String)
m_Value = Value
End Set
End Property
End Class
And in our program we have two functions:
Public Sub DoWork(ByVal obj as Foo)
obj = Nothing
End Sub
Public Sub DoWorkRef(ByRef obj as Foo)
obj = Nothing
End Sub
And we call them like this:
Dim obj1 as new Foo()
Dim obj2 as new Foo()
obj1.Value = "bar"
obj2.Value = "baz"
DoWork(obj1)
DoWorkRef(obj2)
At the end, this function obj1
will still matter, but obj2
will Nothing
. This is due to the fact that it obj1
is passed by value, so the code in DoWork
works on a copy of that variable (again, this is the same instance, it is just a variable that is different), whereas it obj2
is passed by reference, so it points to the same variable as and the main code.
To indicate "the same instance", let's say we changed the functions as follows:
Public Sub DoWork(ByVal obj as Foo)
obj.Value = "beep"
End Sub
Public Sub DoWorkRef(ByRef obj as Foo)
obj.Value = "bop"
End Sub
If we were to run the same code again, we would end up with obj1.Value
equal "beep" and obj2.Value
equal "bop". This is because although we are passing obj1
by value, this value is a reference. You now have two variables pointing to the same instance, so whatever is done will make that instance reflected in both variables.
It's important to remember that the only effective difference between ByRef
and ByVal
occurs when you assign a new value to the variable itself . All other actions are virtually the same.
Editing after reply
You are not passing a variable as a parameter ByRef
: you are passing a property. While C # will not allow this (due to this very problem), VB.NET will. If you pass the property as a parameter ByRef
, essentially how to do it:
Dim _temp as String = b.Name
Foo(_temp)
b.Name = _temp
In other words, when a property is passed as a parameter ByRef
, the setter for that property is always invoked using the value present in the variable after the function is executed, even if the value has not changed.
It's a good rule of thumb not to pass properties as parameters ByRef
.
a source to share
Well yes, if you change from parameter declaration ByRef
to ByVal
, it changes the behavior that breaks your code. This is one of the reasons why, in C #, it must also be specified in the calling code.
If there was no difference between ByRef
and ByVal
, we would not have become both of them.
I have an article on parameter passing that can help you understand the difference between the two; it is written in C # but the basic principles are the same.
a source to share
Two things stood out to me:
this method generated errors because some strange .NET Runtime thing changed the bar object by calling Dispose () al.
and
someone replaced ByRef ByVal and the object hasn't changed anymore
Remember that variables for reference types in .Net work the same way as pointers, since a variable is just a kind of handle to an actual object in memory. When you pass this object to the ByVal function (the default in .Net, and you shouldn't change from the default unless you know the implications), you're making a copy of the handle or reference. When you pass a ByRef object, you are passing the reference yourself.
This means that if you have to do something like assigning a reference to another object to your bar variable in that function, you also changed the original variable outside the function. Even though your function never changed the object, by doing this assignment, you also changed the object reference outside the function, and thus probably lost that confirmation - hence the object was deleted.
a source to share