ByRef vs ByVal会产生错误吗?

时间:2010-04-26 14:10:40

标签: .net vb.net .net-2.0

ByRef vs ByVal会产生错误!?

我有一个使用Object

的方法
Function Foo(ByRef bar as CustomObject) as Boolean

此方法生成错误,因为一些奇怪的.NET运行时事件改变了bar对象,导致其Dispose()al。

花了很多时间来理解这个东西(...对象被更改的地方),直到有人用ByRef替换ByVal并且传递给这个方法时对象不再改变。 ..

有人可以解释一下,会发生什么?

Nota Bene(编辑)

作为在我的情况下,函数Foo执行不修改bar ,不应该ByRef或{{1是否具有相同的效果?

ByVal只是从Foo读取了属性。

代码:

bar

输出:

  

姓名是:'约翰'   名称已更改为“John”

4 个答案:

答案 0 :(得分:6)

传递参数ByRef意味着如果有人为变量分配了一个新值,那么新值将被传递回调用函数。传递它ByVal会将该值的副本传递给该函数,因此更改不会传播回调用者。

请注意,当我引用时,它实际存储在该变量中。使用引用类型,这意味着它是引用。 按值传递引用类型不会复制整个实例,只会复制引用。这意味着对对象本身所做的任何更改仍将对调用函数可见。

例如,考虑我们有这个类:

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

在我们的计划中,我们有两个功能:

Public Sub DoWork(ByVal obj as Foo)
    obj = Nothing
End Sub

Public Sub DoWorkRef(ByRef obj as Foo)
    obj = Nothing
End Sub

我们称之为:

Dim obj1 as new Foo()
Dim obj2 as new Foo()

obj1.Value = "bar"
obj2.Value = "baz"

DoWork(obj1)
DoWorkRef(obj2)

在此功能结束时,obj1仍会有值,但obj2将为Nothing。这是因为obj1是按值传递的,因此DoWork中的代码在该变量的副本上运行(同样,它是相同的实例,它只是变量这是不同的),而obj2是通过引用传递的,所以它指向与主代码相同的变量。

要指出“同一个实例”,假设我们将函数更改为:

Public Sub DoWork(ByVal obj as Foo)
    obj.Value = "beep"
End Sub

Public Sub DoWorkRef(ByRef obj as Foo)
    obj.Value = "bop"
End Sub

如果我们再次运行相同的代码,我们最终会obj1.Value等于“哔”,而obj2.Value等于“bop”。这是因为即使我们按值传递obj1值也是参考。您现在只有两个变量指向同一个实例,因此完成的任何操作都将反映在两个变量中。

要记住的重要一点是,当您为变量本身分配新值时,ByRefByVal之间的唯一有效差异就会出现。所有其他行为实际上都是一样的。

提问后编辑

您没有将变量作为ByRef参数传递:您正在传递属性。虽然C#不允许这样(因为这个问题),VB.NET会允许它。如果您将属性作为ByRef参数传递,则基本上就像这样做:

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

换句话说,当将属性作为ByRef参数传递时,该属性的setter在执行函数后使用变量中存在的值始终调用,即使价值没有改变。

将属性作为ByRef参数传递是一个很好的经验法则而不是

答案 1 :(得分:4)

在VB.NET中,通过引用传递属性会更新实际属性,而不是基础值。因此,当Foo完成时,CLR调用Property Set方法以使用函数末尾的新值更新属性值(即使它没有更改)。

这种行为在VB.NET语言规范(参考参数部分,最后3段)中描述:

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

答案 2 :(得分:1)

是的,如果您将参数声明为ByRef更改为ByVal,则会改变行为 - 这会破坏您的代码。这就是为什么在C#中也必须在调用代码中指定的原因之一。

如果ByRefByVal之间没有差异,我们就不会同时考虑它们。

我有article about parameter passing可以帮助您了解它们之间的区别;它是用C#编写的,但核心原则是一样的。

答案 3 :(得分:0)

有两件事让我感到高兴:

  

这个方法生成错误,因为一些奇怪的.NET运行时事物改变了bar对象,导致其Dispose()al。

  

有人用ByVal取代了ByRef,对象不再改变了

请记住,.Net中的引用类型的变量与指针的工作方式很相似,因为变量只是某种处理内存中实际对象的一种句柄。当您将此对象传递给函数ByVal(.Net中的默认值,并且您不应该更改默认值,除非您知道其含义),您可以复制句柄或引用。传递对象ByRef时,会传递引用本身。

这意味着如果您要执行某些操作,例如将不同对象的引用分配给函数中的bar变量,您也可以在函数外部更改原始变量。即使你的函数永远不会改变对象,通过进行该赋值你也改变了对函数外部对象的引用,并且这样做可能会丢失该refence - 因此对象被处理掉了。