VBA中ByVal和 ByRef有啥区别?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VBA中ByVal和 ByRef有啥区别?相关的知识,希望对你有一定的参考价值。

ByRef:

按地址传递(ByRef),是指主程序直接将数据交给子过程(过程中定义传递方式),在过程中修改、调试、返回给主程序,主程序输出的是修改后的值。

例如:如果我在参数中设为ByRef,那么ByRef的这个变量我可以改变它的值


ByVal:


按值传递(Byval),是指将主程序的值(副本一份)给子过程,过程对副本操作,主程序输出的仍是原值。若想输出值传递后的值,可在子过程中设置输出。

例如:如果我在参数中设为ByRef,那么ByRef的这个变量我不可以改变它的值,改变的只是它的副本,没有任何意义


下面是一个实例:


Sub Test(ByRef a As Integer, ByVal b As Integer) 
'注意,此处a是按地址传递,b是按值传递
a = 3
b = 4
End Sub

'这里是主程序
Sub Main()
Dim a As Integer
Dim b As Integer
a = 1
b = 2
Test(a, b)
MsgBox ("a=" & a & ";b=" & b)
End Sub

以上代码运行之后的结果是

a=3;b=2

这说明,ByRef 可以改变传入的值,而ByVal不行

参考技术A ByVal表示该参数按值传递。ByRef表示该参数按地址传递。ByRef 是 Visual Basic 的缺省选项。按值:一种将参数值而不是将地址传递给过程的方式,这就使过程访问到变量的复本。结果,过程不可改变变量的真正值。按地址:一种将参数地址而不是将值传递给过程的方式,这就使过程访问到实际的变量。结果,过程可改变变量的真正值。除非另作说明,否则按地址传递参数。简明一点说:以ByVal传递给过程,过程里有代码对这个变量做了更改,在过程结束返回后这个变量还是调用过程前的值,不会变化。以ByRef传递给过 程,过程里有代码对这个变量做了更改,在过程结束返回后这个变量的值就是过程里改成的那个值。下面举个例子:(1)ByVal示例Sub TmpByVal()Dim a As Stringa = "2"Call FunByVal(a)MsgBox CStr(a)End SubFunction FunByVal(ByVal a)a = "3"End Function(2)ByRef示例Sub TmpByRef()Dim a As Stringa = "2"Call FunByRef(a)总结:vb(vba)中,除非你有足够的理由和有了足够的准备接受传址得来的值,都要使用ByVal来传值,ByRef意味梦魇的开始!但不要因为怕 ByRef就不用,而是要注意出现梦魇时要想到那可能是使用ByRef的结果。本回答被提问者采纳

ByVal 和 ByRef 的区别?

【中文标题】ByVal 和 ByRef 的区别?【英文标题】:Difference between ByVal and ByRef? 【发布时间】:2011-06-21 23:25:37 【问题描述】:

有什么区别?我总是使用 ByVal,但是,我真的不知道什么时候应该什么时候不应该......

【问题讨论】:

95% 以上的时间您都需要 ByVal,因此您可能一直在做出正确的选择 :) 但是 +1 表示主动了解它。 前言:我不是VB程序员。如果它是 C 或 C++ 之类的东西,如果对象的复制成本很高,则传递 ByVal 可能会很昂贵。如果您知道不打算修改它,ByRef 可能会更快,并且函数的行为会相同。 我在下面评论了同样的事情,但以防万一...... ByVal 创建对象的副本(值类型变量除外)。它创建对同一对象的新引用。你是对的,ByRef 可能更快(不必创建新的引用),但差异充其量是微不足道的。 【参考方案1】:

ByRef = 你给你的朋友你的学期论文(原件),他做了标记,可以退还给你。

ByVal = 你给他一份学期论文,他还给你他的修改,但你必须自己把它们放回原件。

尽可能简单。

为什么要使用 ByRef: ByRef 会将指针传递给您正在传递的对象。如果您在同一个内存空间中,这意味着只传递“单词”而不是对象。您传递给它的方法可以在原始对象中进行更改,并且根本不需要将它们传递回来,因为它们在原始对象中。有助于加快大数据传递。您还可以使用 ByRef 来允许使用 SUB 而不是 FUNCTION(在 VB 中),因为它不需要传回对象。

为什么不使用 ByRef: 由于该方法可以访问原始方法,因此所做的任何更改都将是即时且永久的。如果该方法失败,则该对象可能已损坏。使用 ByVal 将制作一个副本,将整个副本传递给方法,然后该方法将处理信息并返回一个副本、报告信息或什么都不做。

【讨论】:

我几乎完全不同意。使用 ByVal 不会复制对象。它会创建一个指向 same 对象的新引用。n 您不能对原始引用执行任何操作,但可以修改该对象。尝试将新引用设置为空并不会释放对象,因为先前的引用仍然存在(这使对象在 GC 眼中保持活动状态) 我相信你是对的,但不是对的 :) 对于 By Val,新对象具有你正在调用的过程的范围,并且在过程的范围内是完全可编辑的。一旦处理离开过程,对象就会超出范围并被回收(并且无法使用)。因此,当使用 val 时,它确实会生成您传入的对象的完整副本。通过 ref 将指向的对象传递给对象,并且不会创建对象的额外副本。 @TomVandeStouwe:你错了。没有创建新对象。这很容易以多种不同的方式进行测试(从使用一个根本无法多次放入内存的巨大对象,到查看哈希码)。【参考方案2】:

如果传入引用,当你修改方法中的值时,调用站点中的变量也会被修改。

如果你传递值,它就像在方法中创建另一个变量一样,所以即使你修改它,原始变量(在调用站点)的值也不会改变。

因此,实际上,您通常应该将变量作为值传递。仅在您有明确需要时作为参考传递。

【讨论】:

我认为非常重要的是要注意,虽然原始对象在传递byval时无法更改,但它的子成员可以 确实,Smudge202,这很重要……您可以改变对象成员(或者,换句话说,您可以改变对象)。你不能改变的是引用,这意味着你不能用新对象或 null 替换对象。 +1 用于澄清您的答案。另外,请参阅下面我的帖子中的编辑以了解 有趣的【参考方案3】:

ByRef 就像第二个返回值。它将对对象的引用传递给函数而不是对象本身。如果您在函数中更改ByRef 参数的值,您将在函数结束后看到这些更改。如果这还不够清楚,read this 和 this。

【讨论】:

【参考方案4】:

我知道这个问题已经回答得差不多了,但我只想添加以下内容...

您传递给函数的对象受 ByRef/ByVal 约束,但是,如果该对象包含对其他对象的引用,则它们可以被调用的方法修改,而不管 ByRef/ByVal。解释不好,我知道,请参阅下面的代码以获得更好的理解:

Public Sub Test()
    Dim testCase As List(Of String) = GetNewList()
    ByRefChange1(testCase)
    'testCase = Nothing
    testCase = GetNewList()

    ByValChange1(testCase)
    'testCase is unchanged
    testCase = GetNewList()

    ByRefChange2(testCase)
    'testCase contains the element "ByRef Change 2"
    testCase = GetNewList()

    ByValChange2(testCase)
    'testCase contains the element "ByVal Change 2"

End Sub

Public Function GetNewList() As List(Of String)
    Dim result As List(Of String) = New List(Of String)
    result.Add("Value A")
    result.Add("Value B")
    result.Add("Value C")
    Return result
End Function

Public Sub ByRefChange1(ByRef aList As List(Of String))
    aList = Nothing
End Sub

Public Sub ByValChange1(ByVal aList As List(Of String))
    aList = Nothing
End Sub

Public Sub ByRefChange2(ByRef aList As List(Of String))
    aList.Add("ByRef Change 2")
End Sub

Public Sub ByValChange2(ByVal aList As List(Of String))
    aList.Add("ByVal Change 2")
End Sub

编辑:

另外,考虑一下这个函数是否被调用:

Public Sub ByValChange3(ByVal aList As List(Of String))
    aList.Add("ByVal Change 3")
    aList = New List(Of String)
    aList.Add("ByVal Change 4")
End Sub

在这种情况下发生的情况是“ByVal Change 3”被添加到调用者列表中,但是在您指定“aList = New List”时,您将新引用指向一个新对象,并变得分离从来电者列表中。既是常识,也可能有一天会被你发现,所以要记住一些事情。

【讨论】:

【参考方案5】:

我希望这能回答你的问题

Sub last_column_process()
Dim last_column As Integer

last_column = 234
MsgBox last_column

trying_byref x:=last_column
MsgBox last_column

trying_byval v:=last_column
MsgBox last_column

End Sub

Sub trying_byref(ByRef x)
x = 345
End Sub

Sub trying_byval(ByRef v)
v = 555
End Sub

【讨论】:

【参考方案6】:

认为上一个示例中可能存在拼写错误: 最后一个子应该是“byval”而不是“byref”。 :)

还在try_byval 中添加了一个msgbox 语句,以便您了解其含义。

Sub begin()
Dim last_column As Integer

last_column = 234
MsgBox "Begin:" & last_column

trying_byref x:=last_column
MsgBox "byref:" & last_column

trying_byval v:=last_column
MsgBox "byval:" & last_column
End Sub

Sub trying_byref(ByRef x)
x = 111
End Sub

Sub trying_byval(ByVal v)  '<--not ByRef, that was in sub trying_byref.
v = 222
MsgBox "In Here:" & v
End Sub

【讨论】:

【参考方案7】:

ByRef,一个值有2个地址

所以如果 x=80(80 是值,x 是地址,那么例如变量 y 也可以是 80,因此 80 可以被 x 和 y 访问)

【讨论】:

【参考方案8】:

@Tom 和@kelloti 的回答很有帮助。下面是一个代码示例来进一步说明:

Private Function ValMessage(ByVal SomeMessage As String)
    SomeMessage = "Val Val Val" ' <-- this variable modification doesn't persist after the function finishes execution
    ValMessage = "Some Return Value"
End Function

Private Function RefMessage(ByRef SomeMessage As String)
    SomeMessage = "Ref Ref Ref" ' <-- this variable modification persists even after the function finishes execution
    RefMessage = "Some Return Value"
End Function

Private Sub DoStuff()
    Dim OriginalMessage As String
    Dim OtherMessage As String
    Dim AnotherMessage As String

    OriginalMessage = "Original"
    MsgBox ("ORIGINAL: " & OriginalMessage) '--> "Original"

    OtherMessage = ValMessage(OriginalMessage)
    MsgBox ("ORIGINAL: " & OriginalMessage) '--> "Original"

    AnotherMessage = RefMessage(OriginalMessage)
    MsgBox ("ORIGINAL: " & OriginalMessage) '--> "Ref Ref Ref" <--- this is the difference when you pass a paramter by reference
End Sub

【讨论】:

【参考方案9】:

我会尽量用简单的词来解释区别。

按值传递参数使其仅作为输入参数。这是最安全的方式,因此在 95% 的情况下默认使用。

通过引用传递参数使其既是输入参数又是输出参数。可以在函数内部更改输出参数,这会产生很少使用的副作用。

【讨论】:

以上是关于VBA中ByVal和 ByRef有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

optional [byval byref] [paramarray] 变量名() as 数据类型

Visual Basic 6.0中ByVal和ByRef的区别和应用示例!optional和缺省时的区别和应用示例!

VBA Object对象的函数参数传递

ctypes.pointer、ctypes.POINTER 和 ctypes.byref 有啥区别?

VBA学习笔记之过程

VBA的有参过程定义,形参用啥说明