.net ByRef vs ByVal生成错误?

.net ByRef vs ByVal生成错误?,.net,vb.net,.net-2.0,.net,Vb.net,.net 2.0,ByRefvsByVal生成错误 我有一个使用对象的方法 Function Foo(ByRef bar as CustomObject) as Boolean 此方法生成错误,因为某些奇怪的.NET运行时操作更改了bar对象,导致其Dispose()异常 花了很多时间来理解这个东西(对象被更改的地方),直到有人用ByRef替换了ByVal并且对象在传递到这个方法时不再更改 有人可以解释,会发生什么 Nota Bene(编辑) 在我的例子中,函数Foo不会修改条,那么ByRef或ByVal是否应

ByRef
vs
ByVal
生成错误

我有一个使用对象的方法

Function Foo(ByRef bar as CustomObject) as Boolean
此方法生成错误,因为某些奇怪的.NET运行时操作更改了
bar
对象,导致其Dispose()异常

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

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

Nota Bene(编辑)
在我的例子中,函数
Foo
不会修改
,那么
ByRef
ByVal
是否应该具有相同的效果

Foo
只需从
bar
读取属性即可

代码:

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
输出:

名字是:“约翰”
姓名改为“约翰”


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

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

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

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)
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
仍将有一个值,但
obj2
将为
Nothing
。这是因为
obj1
是按值传递的,因此
DoWork
中的代码操作该变量的副本(同样,它是同一个实例,只是变量不同),而
obj2
是按引用传递的,因此它指向与主代码相同的变量

为了指出“相同的实例”,假设我们将函数更改为:

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

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

DoWork(obj1)
DoWorkRef(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
等于“beep”,而
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
参数传递是一条很好的经验法则。

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

如果
ByRef
ByVal
之间没有区别,我们就不会麻烦把它们都拥有了


我有一个可以帮助你理解它们之间区别的方法;它是用C#写的,但核心原则是一样的。

我觉得有两件事很突出:

此方法生成错误,因为某些奇怪的.NET运行时操作更改了bar对象,导致其Dispose()异常

有人用ByVal替换了ByRef,对象不再改变了

请记住.Net中引用类型的变量与指针非常相似,因为变量只是内存中某个位置的实际对象的一种句柄。当您将此对象传递给函数ByVal(在.Net中为默认值,除非您知道其含义,否则不应更改默认值)时,您将创建句柄或引用的副本。传递对象ByRef时,传递引用本身


这意味着,如果要将对不同对象的引用指定给函数中的bar变量,那么也会在函数外部更改原始变量。即使您的函数从未更改过对象,但通过进行该赋值,您也更改了对函数外部对象的引用,这样做很可能会丢失该引用,因此对象被释放。

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

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


+1。。。你刚才让我删除了我的答案,并对你的答案投了赞成票。:-)在我的例子中,函数
Foo
不会修改
,因此,我认为
ByRef
ByVal
应该是等效的…@serhio:你的意思是
Foo
不会修改
obj
?您是否将其传递给使用
ByRef
参数的任何其他函数?您是否可以发布(部分)Foo供我们查看?这是