ByRef
vs ByVal
会产生错误!?
我有一个使用Object
的方法Function Foo(ByRef bar as CustomObject) as Boolean
此方法生成错误,因为一些奇怪的.NET运行时事件改变了bar
对象,导致其Dispose()al。
花了很多时间来理解这个东西(...对象被更改的地方),直到有人用ByRef
替换ByVal
并且传递给这个方法时对象不再改变。 ..
有人可以解释一下,会发生什么?
作为在我的情况下,函数Foo
执行不修改bar
,不应该ByRef
或{{1是否具有相同的效果?
ByVal
只是从Foo
读取了属性。
代码:
bar
输出:
姓名是:'约翰' 名称已更改为“John”
答案 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
,值也是参考。您现在只有两个变量指向同一个实例,因此完成的任何操作都将反映在两个变量中。
要记住的重要一点是,当您为变量本身分配新值时,ByRef
和ByVal
之间的唯一有效差异就会出现。所有其他行为实际上都是一样的。
提问后编辑
您没有将变量作为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#中也必须在调用代码中指定的原因之一。
如果ByRef
和ByVal
之间没有差异,我们就不会同时考虑它们。
我有article about parameter passing可以帮助您了解它们之间的区别;它是用C#编写的,但核心原则是一样的。
答案 3 :(得分:0)
有两件事让我感到高兴:
这个方法生成错误,因为一些奇怪的.NET运行时事物改变了bar对象,导致其Dispose()al。
和
有人用ByVal取代了ByRef,对象不再改变了
请记住,.Net中的引用类型的变量与指针的工作方式很相似,因为变量只是某种处理内存中实际对象的一种句柄。当您将此对象传递给函数ByVal(.Net中的默认值,并且您不应该更改默认值,除非您知道其含义),您可以复制句柄或引用。传递对象ByRef时,会传递引用本身。
这意味着如果您要执行某些操作,例如将不同对象的引用分配给函数中的bar变量,您也可以在函数外部更改原始变量。即使你的函数永远不会改变对象,通过进行该赋值你也改变了对函数外部对象的引用,并且这样做可能会丢失该refence - 因此对象被处理掉了。