VBA 中 ByVal 和 ByRef 的基础用法和区别

VBA 中定义过程函数时,如果需要传递变量,需指定参数的传递类型,包括以下 2 类:

  • ByVal:传递参数的值
  • ByRef:传递参数的引用

本篇将介绍 2 种方法的用法以及区别。过程和函数传递参数方法基本相同,本篇以过程(Sub)举例说明他们的用法和区别。

ByVal 和 ByRef 基础

在定义过程或函数时,如果需要传递变量,则每个参数需要指定传递类型。传递类型有 2 种,分别是 ByValByRef

'ByVal 传递类型
Sub TestSub1(ByVal msg As String)

End Sub

'ByRef 传递类型
Sub TestSub2(ByRef msg As String)

End Sub

针对基础数据类型,例如数字、文本等,两种传递类型的说明和区别如下:

  • ByVal:传递变量时,复制一份该变量,传入过程或函数。在过程和函数内部对该变量进行修改,只对该副本有效,对上一级过程(父过程)的变量没有影响。
  • ByRef:传递变量时,将该变量的引用地址传入过程或函数。传入引用地址意味着,在过程或函数内部对其修改时,也会影响上一级过程(父过程)中的变量的值。

ByVal 实例

通过以下代码测试 ByVal 类型:

Sub Test()

    Dim msg As String
    msg = "main"
    
    TestSub1 msg
    
    Msgbox msg

End Sub

'ByVal 传递类型
Sub TestSub1(ByVal msg As String)
    msg = "val"
End Sub

首先定义一个 msg 变量,赋值 main,然后调用 TestSub1 过程,传入 msg 变量,在过程内部对 msg 重新赋值 val。最后返回上一个过程,显示 msg 变量。结果如下,msg 变量的值没有改变。

ByRef 实例

通过以下代码测试 ByRef 类型:

Sub Test()

    Dim msg As String
    msg = "main"
    
    TestSub2 msg
    
    MsgBox msg

End Sub

'ByRef 传递类型
Sub TestSub2(ByRef msg As String)
    msg = "ref"
End Sub

首先定义一个 msg 变量,赋值 main,然后调用 TestSub2 过程,传入 msg 变量,在过程内部对 msg 重新赋值 ref。最后返回上一个过程,显示 msg 变量。结果如下,msg 变量的值已改变。

省略传递类型

默认情况下,当省略传递类型时,默认值是 ByRef,因此以下两种写法是等效的。

'指定 ByRef 传递类型
Sub TestSub1(ByRef msg As String)

End Sub

'省略传递类型
Sub TestSub1(msg As String)

End Sub

使用 ByVal 和 ByRef 传递对象

在上述介绍中说道,以上机制适用于传递基础类型变量,例如数字、文本、逻辑值等。

使用 ByVal 和 ByRef 传递对象时,情况有些不同。具体用法和不同点将在介绍对象时详细说明。

使用 ByVal 和 ByRef 传递数组

过程或函数传递数组时,只能以引用形式传递,即以 ByRef 形式。如果尝试用 ByVal 传递数组,VBA 会提示错误。详细的用法将在介绍数组时详细说明。

总结

ByVal 和 ByRef 表示参数传递的类型。针对基础数据类型的变量,ByVal 会创建变量的一个副本,传递给过程或函数,从此之后与父过程的变量没有关系。而 ByRef 方式传递变量的引用,该引用始终会与父过程的变量相连。

因此建议,尽量使用 ByVal 传递类型,防止在子过程或函数中,不小心更改父过程里的变量,导致一些不容易发现的问题。

对象和数组变量的传递,有别于基础类型变量,在相关的教程中详细说明。

转载注明:文章转载自「懒人Excel - www.lanrenexcel.com
订阅评论
提醒
32 评论
最新
最旧 最多投票
内联反馈
查看所有评论
123
5 月 前

精彩

tony
5 月 前

Sub Test()
Dim name As String
Dim age As Double
name = “sam”
age = 15
exam name
exam age

msgbox name & age
End Sub
Sub exam(ByRef name As String, ByRef age As Double)
name = “peter”
age = 10

End Sub

這個 Compile error: Argument not optional,不太明白,想請教一下,感謝

moumou
6 月 前

‘指定 ByVal 传递类型
Sub TestSub1(ByRef msg As String)
这里是不是写错了,要写‘指定ByRef传递类型

哈哈哈哈
9 月 前

Sub TestBy()

  Dim msg As String
  msg = “Test”
  SubByVal (msg)
  Debug.Print “After call SubByVal() msg = ” & msg
  SubByRef (msg)
  Debug.Print “After call SubByRef() msg = ” & msg

End Sub

Sub SubByVal(ByVal msg As String)

  msg = “ByVal”
  Debug.Print “In SubByVal() msg = ” & msg

End Sub

Sub SubByRef(ByRef msg As String)

  msg = “ByRef”
  Debug.Print “In SubByRef() msg = ” & msg

End Sub

执行结果
In SubByVal() msg = ByVal
After call SubByVal() msg = Test
In SubByRef() msg = ByRef
After call SubByRef() msg = Test

好像值没有变呢

maninshu
6 月 前
回复给  懒人Excel

Function Examv_al(Optional ByVal x As Integer = 2) As Integer
  x = x + 1
  Debug.Print x
  Examv_al = x
End Function

Function Examr_ef(Optional ByRef y As Integer = 2) As Integer
  y = y + 1
  Debug.Print y
  Examr_ef = y
End Function
我这里的两个过程值也是完全一样的。
不提供参数,立即窗口都是3,单元格里也都是3.
提供参数9,立即窗口都是10,单元格里也都是10.

最后由maninshu编辑于6 月 前
冷月Des
11 月 前

和 C++ 很像很像 , 有C++ 基础, 半天就从头看到了这里, 讲的蛮清楚的

竹蜻蜓
1 年 前

作者,还有在更新吗?

Joann
1 年 前

作者,你好,还在吗?ByVal 的那个实例不是很明白,为什么出来的结果是 main 的? 下面的那个 sub 又什么意义呢?

Joann
1 年 前
回复给  懒人Excel

谢谢,作者,但还是不是很明白。什么样的情况下会用到这两种用法?

Stoner
1 年 前
回复给  Joann

舉個例子 如果 你寫了一個變數互換的function,但是傳遞參數是Byval

這樣一來

Sub Test()

  Dim a,b as String
   
  a = “A”
b = “B”
   
  swap a,b

End Sub

Sub swap(ByVal a As String, ByVal b As String)
  tmp = a
a = b
b = tmp
End Sub

a跟b仍然是”A”跟”B”

但是使用ByRef的話
Sub Test()

  Dim a,b as String
   
  a = “A”
b = “B”
   
  swap a,b

End Sub

Sub swap(ByRef a As String, ByRef b As String)
  tmp = a
a = b
b = tmp
End Sub

結果就達成了a是”B” b是”A”

闪电战
1 年 前

ByRef就相当于C里面的传指针

小菜鸟
1 年 前

23年了

菜头啊
2 年 前

作者你还在吗
我们还在等你

CCCC
2 年 前

请问,为什么在 TestSub2 (msg)这里加上括号,和不加括号,返回的结果不一样呢?

Sub Test()
   
  Dim msg As String
   
  msg = “main”
   
  TestSub2 (msg) ‘add (), returns different result, suppose to be ref, but main
   
  MsgBox msg
   
End Sub

‘ByRef
Sub TestSub2(ByRef msg As String)
  msg = “ref”
End Sub

方寸雷
2 年 前

我理解的是使用 ByRef的话就相当于函数有返回值

曾经的程序员
3 年 前

默认值是ByRef吧

Haechi
2 年 前

不对吧,我看官方文档写的是默认ByVal
引用如下
https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/modifiers/byval

If no modifier is specified, ByVal is the default.

Note
Because it is the default, you do not have to explicitly specify the ByVal keyword in method signatures. It tends to produce noisy code and often leads to the non-default ByRef keyword being overlooked.

大宝徐
3 年 前

这是最后一篇了,我都看完了,受益匪浅,感谢!
最近碰到一个问题请教下,我在Excel里面操作word中表格发现设置的标题行重复属性已经设置好了,但Word文件打开没有看到标题行重复。
Windows 10,Office 2010 32位。在excel中打开一个word文件然后save2存为一个新文件。打开这个新word文件,对表格导入数据,然后设置标题行重复和允许跨页中断,然后保存、关闭。
‘设置行标题重复
    WordTable.Rows.HeadingFormat = True
    WordTable.Rows.AllowBreakAcrossPages = True

请问,是否还有其他问题?

Anna^
3 年 前

这节有点看不明白:什么情况下会使用到传递参数呢,能列举实际应用中的示例吗~感谢博主

最后由Anna^编辑于3 年 前
32
0
希望看到您的想法,请您发表评论x