Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ByRef vs ByVal generates errors?

ByRef vs ByVal generates errors!?

I had a method that used an Object

Function Foo(ByRef bar as CustomObject) as Boolean

this method generated errors, because some strange .NET Runtime things changed the bar object, causing its Dispose()al.

A lot of time spent to understand the thing(where the ... object is changed), until somebody replaced ByRef by ByVal and object wasn't change anymore when passing to this method...

Somebody could explain this, what happens?

Nota Bene (edit)

As in my case the function Foo does NOT modify the bar, shouldn't ByRef or ByVal have the same effect?

The Foo just read the Properties from bar.

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 is : 'John'
Name is Changed to 'John'

like image 973
serhio Avatar asked Mar 23 '26 14:03

serhio


2 Answers

Passing an argument ByRef means that if someone assigns a new value to the variable, that new value will be passed back to the calling function. Passing it ByVal passes a copy of that value to the function, so changes are not propagated back to the caller.

Note that when I refer to the value, it's what's actually stored in that variable. With reference types, that means that it's the 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 we have 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 of this function, obj1 will still have a value, but obj2 will be Nothing. This is because obj1 is being passed by value, so the code in DoWork is operating on a copy of that variable (again, it's the same instance, it's just the variable that's different), whereas obj2 is being passed by reference, so it's pointing at the same variable as the main code.

To point out the "same instance", let's say we changed the functions to be like this:

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 over again, we'd end up with obj1.Value being equal to "beep", and obj2.Value being equal to "bop". This is because even though we're passing obj1 by value, the value is a reference. You now just have two variables pointing at the same instance, so anything done do that instance will be reflected in both variables.

The important thing to remember is that the only effective difference between ByRef and ByVal comes when you assign a new value to the variable itself. All other behavior is effectively the same.

Post-Question-Edit Edit

You aren't passing a variable as the ByRef parameter: you're passing a property. While C# will not allow this (because of this very issue), VB.NET will allow it. If you pass a property as a ByRef parameter, it's essentially like doing this:

Dim _temp as String = b.Name
Foo(_temp)
b.Name = _temp

In other words, when passing a property as a ByRef parameter, the setter for that property is always called using the value present in the variable after executing the function, even if the value didn't change.

It's a good rule of thumb not to pass properties as ByRef parameters.

like image 152
Adam Robinson Avatar answered Mar 25 '26 02:03

Adam Robinson


In VB.NET, passing a property by reference updates the actual property, not the underlying value. Thus, when Foo completes, the CLR calls the Property Set method to update the property value with whatever the new value is at the end of the function (even if it hasn't changed).

This behaviour is described in the VB.NET Language Specification (Reference Parameters section, last 3 paragraphs):

http://msdn.microsoft.com/en-us/library/aa711958(v=VS.71).aspx

like image 36
goofballLogic Avatar answered Mar 25 '26 04:03

goofballLogic



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!