开发者

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 ..开发者_StackOverflow中文版. 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'


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.


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


Well yes, if you change from declaring a parameter as ByRef to ByVal, that will change the behaviour - which make break your code. That's one reason why in C# it has to be specified in the calling code as well.

If there was no difference between ByRef and ByVal, we wouldn't bother having them both.

I have an article about parameter passing which may help you understand the difference between them; it's written in C#, but the core principles are the same.


Two things stood out to me:

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

and

somebody replaced ByRef by ByVal and object wasn't change anymore

Remember that variables for reference types in .Net work a lot like pointers, in that the variable is only a sort of handle to the actual object off in memory somewhere. When you pass this object to a function ByVal (the default in .Net, and you shouldn't change from the default unless you know the implications), you make a copy of the handle or reference. When you pass the object ByRef, you pass the reference itself.

This means that if you were to do something like assign a reference to a different object to your bar variable in the function, you've changed the original variable outside of the function as well. Even though your function never changed the object, by making that assignment you changed the reference to the object outside of the function as well, and in so doing likely lost that refence -- hence the object is disposed.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜