VBA的过程及参数详解

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

VBA的过程及参数详解
VBA的过程及参数详解
VBA中的过程(Procedure)有两种,⼀种叫函数(Function),另外⼀种叫⼦程序(Subroutine),分别使⽤Function和Sub关键字。

它们都是⼀个可以获取参数、执⾏⼀系列语句、以及改变其参数的值的独⽴过程。

⽽与 Function 过程不同的是:带返回值的 Sub 过程不能⽤于表达式。

这⾥主要介绍⼦程序的使⽤⽅法,同样这些⽅法也可以应⽤到Function上。

语法
[Private | Public | Friend] [Static] Sub name [(arglist)]
[statements]
[Exit Sub]
[statements]
End Sub
* ⽤[]符号括起来的选项是可选项
[Private | Public | Friend]
这三个关键字与作⽤范围有关。

Private表⽰私有,即这个过程只能从本模块⾥⾯调⽤。

使⽤这个关键字,从菜单“⼯具”->”宏”->”宏…”中将看不到该过程。

Public表⽰公⽤,这样从其它的模块也可以访问这个过程。

如果没有使⽤ Public、Private 或 Friend 显式指定,Sub 过程按缺省情况就是公⽤的。

公⽤的过程可以从菜单”⼯具”->”宏”->”宏…”中看到。

Friend⽤在类模块⾥⾯,较少使⽤,在此就不介绍了。

Static是静态的意思(你可能还记得⽤Static声明静态变量),⽤它声明过程的话,表⽰这个过程中声明的局部变量在下次调⽤这个过程时仍然保持它原来的值。

下⾯是Static声明过程的⽤法。

Static Sub m1()
Dim i As Integer
Dim j As Integer
i = i + 1
j = j + 1
Debug.Print "i=" & i & " j=" & j
End Sub
Private Sub m2()
Dim i As Integer
Dim j As Integer
i = i + 1
j = j + 1
Debug.Print "i=" & i & " j=" & j
End Sub
Sub try1()
Dim i As Integer
Debug.Print "静态过程:"
For i = 1 To 10
Call m1
Next i
Debug.Print "私有过程:"
For i = 1 To 10
Call m2
Next i
End Sub
运⾏try1过程,然后可以在⽴即窗⼝⾥看到结果。

=
下⾯是⼀些使⽤过程时需要注意到地⽅。

Sub过程可以是递归的;也就是说,该过程可以调⽤⾃⼰来完成某个特定的任务。

不过,递归可能会导致堆栈上溢。

通常 Static 关键字和递归的 Sub 过程不在⼀起使⽤。

所有的可执⾏代码都必须属于某个过程。

不能在别的 Sub、Function 或 Property 过程中定义 Sub 过程。

Exit Sub 语句使执⾏⽴即从⼀个 Sub 过程中退出。

程序接着从调⽤该 Sub 过程的语句下⼀条语句执⾏。

在 Sub 过程的任何位置都可以有 Exit Sub 语句。

在Sub过程中使⽤的变量分为两类:⼀类是在过程内显式定义的,另⼀类则不是。

在过程内显式定义的变量(使⽤ Dim或等效⽅法)都是局部变量。

对于使⽤了但⼜没有在过程中显式定义的变量,除⾮其在该过程外更⾼级别的位置有显⽰地定义,否则也是局部的。

⼩⼼过程可以使⽤没有在过程内显式定义的变量,但只要有任何在模块级别定义的名称与之同名,就会产⽣名称冲突。

如果过程中使⽤的未定义的变量与别的过程,常数,或变量的名称相同,则认为过程使⽤的是模块级的名称。

显式定义变量就可以避免这类冲突。

可以使⽤ Option Explicit 语句来强制显式定义变量。

注意:不能使⽤ GoSub、GoTo 或 Return 来进⼊或退出 Sub 过程。

参数表arglist
语法:
[Optional] [ByVal | ByRef] [ParamArray] varname[( )] [As type] [= defaultvalue]
Optional表⽰这个参数是可选的,也就是说在调⽤过程时可以不传递值也可以传递值给这个参数.。

如果有传递defaultvalue给这个参数时(如optional iInput2 As Integer=13),当调⽤过程没有传递值给这个参数时,在过程中会默认使⽤这个defaultvalue。

Optional必须对最后⾯的那些参数使⽤,也就是说某个参数使⽤了Optional,该参数后⾯的参数也必须使⽤Optional。

Sub mmm(iInput1 As Integer, Optional iInput2 As Integer = 13, Optional iInput3 As Integer)
'iInput2⽤了Optional后,iInput3也必须⽤Optional
MsgBox "iInput1=" & iInput1 & vbCrLf & "iInput2=" & iInput2 & vbCrLf & "iInput3=" & iInput3
End Sub
Sub subTry()
' 可以给3个参数都赋值
Call mmm(23, 34, 2) 'iInput=23, iInput2=34, iInput3=2
'也可以给第3个赋值,⽽不给第2个参数赋值,这样iInput2会等于默认值13
Call mmm(23, , 2) 'iInput=23, iInput2=13, iInput3=2
'可以给第2个参数都赋值⽽不给第3个参数赋值
Call mmm(23, 34) 'iInput=23, iInput2=34, iInput3=0
' 第2个,第3个参数都不赋值
Call mmm(23) 'iInput=23, iInput2=12, iInput3=0
End Sub
ParamArray的⽤法
ParamArray只能⽤于 arglist 的最后⼀个参数,指明最后这个参数是⼀个包含Variant类型元素的 Optional 数组,但你传递值给过程时还是使⽤逗号分开多个参数,过程⾥⾯会把找⼏个参数合并成⼀个数组。

使⽤ ParamArray 的好处是你可以提供不定数⽬不定类型的参数给过程。

ParamArray不能与 ByVal,ByRef,或 Optional ⼀起使⽤。

Sub m1(iInput1 As Integer, ParamArray argArr())
Dim strList As String
Dim i As Integer
strList = "iInput1=" & iInput1 & vbCrLf
For i = 0 To UBound(argArr)
On Error Resume Next
strList = strList & "argArr(" & i & ")=" & argArr(i) & vbCrLf
' 如果参数为空,将⽣成错误
If Err.Description <> "" Then
strList = strList & "argArr(" & i & ")=缺失参数" & vbCrLf
Err.Clear ' 清除错误
End If
Next i
MsgBox strList
End Sub
Sub try1()
Call m1(23, 24, 25) ' iInput=23, argArr(0)=24, argArr(1)=25
Call m1(23, 24, , 25) ' iInput=23, argArr(0)=24, argArr(1)=缺失参数, argArr(2)=25
Call m1(23, 24, 64.4, 25, "data") ' iInput=23, argArr(0)=24, argArr(1)=64.4, argArr(2)=25, argArr(3)=data
End Sub
ByVal和ByRef
VBA中默认使⽤ByRef。

ByVal的意思是按值传递参数,因为是按值传递,这个参数在过程⾥⾯的值有变化的话它影响的范围只是在这个过程⾥⾯。

出了过程就没有⽤了。

⽽ByRef是按地址或者说按引⽤传递,传递给过程的实际是这个数值的地址,⽽不是值本⾝,在过程中对改变这个参数也就是改变这个地址的值,这样在过程外⾯也可以看到这个值被改变了。

运⾏下⾯的例⼦可以看到其中的区别。

Sub mmm(ByVal iI1 As Integer, iI2 As Integer)
iI1 = iI1 + 10
iI2 = iI2 + 10
MsgBox "Inside: iI1=" & iI1 & " iI2=" & iI2
End Sub
Sub mySub()
Dim iI1 As Integer
Dim iI2 As Integer
iI1 = 10
iI2 = 12
MsgBox "Before: iI1=" & iI1 & " iI2=" & iI2
Call mmm(iI1, iI2)
MsgBox "After: iI1=" & iI1 & " iI2=" & iI2
' 按顺序分别显⽰
' 显⽰ Before: iI1=10 iI2=12
' 显⽰ Before: iI1=20 iI2=22
' 显⽰ Before: iI1=10 iI2=22
' 过程mmm中修改了iI1和iI2,但是iI1是按值传递,在mmm过程之外的iI1并受影响
' ⽽iI2是按引⽤传递,mmm过程之外的iI2也被改变了
End Sub
另外,如果参数是数组的话,只能使⽤按引⽤传递,因为传递的实际上是数组第⼀个元素的地址。

例如下⾯代码的⽤法。

Sub GetArray(arrTemp() As Integer)
Dim i As Integer
For i = 0 To UBound(arrTemp)
Debug.Print "Item " & i & ": " & arrTemp(i)
Next i
End Sub
Sub PassArray()
Dim arrInt(3) As Integer
arrInt(0) = 1
arrInt(1) = 2
arrInt(2) = 3
arrInt(3) = 4
Call GetArray(arrInt)
' 在⽴即窗⼝打印出
' Item 0: 1
' Item 1: 2
' Item 2: 3
' Item 3: 4
End Sub
但对于对象来说,使⽤ByVal实际上传递的仍然是对对象的引⽤,这样在过程中对象的修改将会影响过程外部对象的值或属性。

如果使⽤ByVal,⽽在过程中创建⼀个新的对象实例,将该对象赋值给传递的对象,则不影响调⽤对象的外部的属性或值。

⽽如果使⽤ByRef,在过程中创建⼀个新的对象实例,将该对象赋给传递的对象,却会影响过程外该对象的属性或值。

Sub TestByValByRef()
Dim objDic As Object
Set objDic = CreateObject("Scripting.Dictionary")
objDic(1) = 100
Debug.Print "byValueTest1"
Debug.Print "原始值: objDic(1)=" & objDic(1)
Call byValTest1(objDic)
Debug.Print "外部值: objDic(1)=" & objDic(1) & vbCrLf
objDic(1) = 100
Debug.Print "byValueTest2"
Debug.Print "原始值: objDic(1)=" & objDic(1)
Call byValTest2(objDic)
Debug.Print "外部值: objDic(1)=" & objDic(1) & vbCrLf
objDic(1) = 100
Debug.Print "byRefTest"
Debug.Print "原始值: objDic(1)=" & objDic(1)
Call byRefTest(objDic)
Debug.Print "外部值: objDic(1)=" & objDic(1)
End Sub
Private Sub byValTest1(ByVal c As Object)
Dim a As Object
Set a = CreateObject("Scripting.Dictionary")
a(1) = 200
Set c = a
End Sub
Private Sub byValTest2(ByVal c As Object)
c(1) = 200
End Sub
Private Sub byRefTest(ByRef c As Object)
Dim a As Object
Set a = CreateObject("Scripting.Dictionary")
a(1) = 200
Set c = a
End Sub
运⾏过程TestByValByRef,将在⽴即窗⼝打印出下⾯的结果。

byValueTest1
原始值: objDic(1)=100
外部值: objDic(1)=100
byValueTest2
原始值: objDic(1)=100
外部值: objDic(1)=200
byRefTest
原始值: objDic(1)=100
外部值: objDic(1)=200
出处:。

相关文档
最新文档