2、过程参数传递
过程通常需要某些程序当前状态的信息才能完成它的工作。程序当前状态信息也包含在调用过程时传递到过程内的变量。当将变量传递到过程时(完成形式参数与实际参数的结合),称变量为参数。
(1)参数的数据类型
过程的参数类型缺省为Variant数据类型。不过,也可以声明参数为其它数据类型。例如:函数接受一个字符串变量和一个整型变量:
Function WhatForLunch(WeekDay As String,Hour As Integer) As String
If WeekDay="Saturday" then WhatForLunch="Fish" ELse WhatForLunch="Chicken" End If If Hour>2 Then WhatForLunch="Sorry,It's too late!"
End Function
(2)按地址传递参数
按地址传递参数过程(也称“引用”)使得过程通过变量的内存地址去访问实际变量的内容。将变量传递给过程时,在过程中可改变变量的值。按地址传递参数在VB中是缺省的。
如果给按地址传递的参数指定数据类型,就必须将这种类型的值传递给参数,也可以给参数传递一个表达式,VB计算表达式,并按要求的类型将值传递给参数。
当变量通过地址传递时,对变量的任何修改都被传递给调用过程。传址参数调用在修改调用过程中的变量方面具有显著的优点,只是不要在过程中无意地修改变量。
例如,下面的子过程声明和调用:
Sub CostPlusInterest(Cost,Total)
Cost=Cost*1.05 'add 5% to Cost Total=Int(Cost) 'then make integer and return
End Sub
调用:
……
Price=100 Total=0 Costplusinterest Price,Total Print Price; "at 5% interest is ";Total
本例中,程序将两个传址变量传递给CostPlusInterest过程:Price和Total。程序员计划在随后的Print方法中使用更新的Total变量,但不慎忘掉了Price变量在过程的中间步骤中被更新,所以程序运行时,产生结果如下:
105 at 5% interest is 105
没有达到预期的效果。
(3)按值传递参数
避免上述问题的一种好的做法是按值传递参数。按值传递参数传递的只是变量的副本。如果过程执行过程中改变了这个值,则所有变动只影响副本而不会影响变量本身。使用ByVal关键字指出参数是按值来传递的。
例如:对上例加以修改,就不会发生上面出现的错误,改动如下:
Sub CostPlusInterest(ByVal Cost,Total)
当使用ByVal声明Cost参数时,就会产生正确的结果:
100 at 5% interest is 105
如果不想依赖于ByVal关键字,也可以换一种方法防止所传递的变量被修改:将变量置于括弧内而把它转换为文字值,如上例中,调用costplusinterest过程的语句为:
costplusinterest(price),total,
同样可以得到正确结果。
再例如,有下面代码:
Option Explicit
Function Multiply(ByVal n As Integer) As Integer Multiply = 1 Do While n > 0 Multiply = Multiply * n '求n! n = n - 1 Loop End Function
Private Sub Form_Click() Dim sum As Integer, i As Integer For i = 5 To 1 Step -1 sum = sum + Multiply(i) Next Print "sum="; sum End Sub
运行后结果为153(5!+4!+3!+2!+1!)。
若将函数的参数n前的“ByVal”去掉,结果为120(5!),这显然不是编者的初衷,是错误的。
究竟什么时候用传值方式,什么时候用传地址方式,没有硬性规定。下面几条规则可供参考:
1)对于整形、长整形或单精度参数,如果不希望过程修改实参的值,则应加上关键字ByVal(按值传送)。而为了提高效率,字符串和数组应通过地址传送。此外,用户定义的类型(记录类型)和控件只能通过地址传送。
2)对于其它数据类型,包括双精度型、货币型和变体数据类型,可以用两种方式传送。但经验证明,此类参数最好用传值方式传送,这样可以避免错用参数。
3)如果没有把握,最好用传值方式传送所有变量(字符串、数组和记录类型变量除外),在编写完程序并能正确运行后,再把部分参数改为传地址,以加快运行速度。这样,即使在删除一些ByVal后,程序不能正确运行,也很容易查出错在什么地方。
(4)使用可选的参数
VB提供了十分灵活和安全的参数传送方式,允许使用可选参数和可变参数。在调用一个过程时,可以向过程传送可选的参数或者任意数量的参数。
在前面的例子中,一个过程的形式参数(形参)是固定的,调用时提供的实际参数(实参)也是固定的。也就是说,如果一个过程有三个形参,则调用时必须按相同的顺序和类型提供三个实参。
在VB中,可以指定一个或多个参数作为可选参数。例如,我们建立一个计算两个数的乘积的过程,它能够有选择的乘以第三个数。在调用时,既可以给它传送两个参数,也可以给它传送三个参数。
为了定义带可选参数的过程,必须在“参数列表”中使用Optional关键字,并在过程体中通过IsMissing函数测试调用时是否传送可选参数。例如:
Sub Multi(fis As Integer, sec As Integer, Optional third) '第三个参数是可选的 Dim n n = fis * sec If Not IsMissing(third) Then '第三个参数是否存在测试 n = n * third End If Print n '结果可以是两个数或三个数的乘积 End Sub
如果用下面的事件过程调用:
Private Sub Form_Click() Multi 10, 20 End Sub
结果是200;如果用下面的事件过程调用:
Private Sub Form_Click() Multi 10, 20, 30 End Sub
结果是6000。
注意:
1)上面的过程只有一个可选参数,也可以有多个,但可选参数必须放在参数列表的最后也就是说,如果指定了可选参数,则参数列表中此参数后面的其它参数也必是可选的。
2)IsMissing函数有一个参数,它就是由Optional指定的形参的名字,其返回值为Boolean类型,如果没有向可选参数传送实参,则IsMissing函数的返回值为True,否则返回值为False。
3)如果实参数目和形参数目相同,可以省去IsMissing函数。例如:
Dim strname As String Dim steaddress As String
Sub listtext(Optional x As String, Optional y As String) List1.AddItem x List1.AddItem y End Sub
Private Sub command1_click() strname = "yourname" '提供了两个参数 steaddress = 12345 Call listtext(strname, steaddress) End Sub
4)在未提供某个可选参数时,实际将该参数作为具有empty值的变体来赋值。可以用IsMissing函数来测试丢失的可选参数。如:
Dim strname As String Dim steaddress As String
Sub listtext(Optional x As String, Optional y As String) List1.AddItem x If Not IsMissing(y) Then List1.AddItem y End If End Sub
Private Sub command1_click() strname = "yourname" Call listtext(strname) '未提供第二个参数 End Sub
5)提供可选参数的值也可以为可选参数指定缺省值。下例中,如果未将可选参数传递到函数过程,则返回一个缺省值。
Sub listtext(x As String, Optional y As Integer = 12345) List1.AddItem x List1.AddItem y End Sub
Private Sub Command1_Click() Dim strname As String strname = "yourname" Call listtext(strname) '未提供第二个参数 End Sub
(5)使用不定量参数(可变参数)
一般说来,过程调用中的参数个数应等于过程说明的参数个数。但用ParamArray(译:预测的)关键字指明时,过程将接受任意个数的参数。
可变参数过程通过ParamArray命令来定义,一般格式为:
Sub 过程名(ParamArray 数组名())
这里的“数组名”是一个形式参数,只有名字和括号,没有上下界。由于省略了变量类型,“数组”的类型默认为Variant。
例1:前面例子中建立的Multi过程可以求两个或三个数的乘积,下面定义的是一个可变参数过程,用这个过程可以求任意多个数的乘积。
Sub Multi(ParamArray Numbers()) n = 1 For Each x In Numbers n = n * x Next x Print n End Sub
可以用任意个参数调用上述过程。例如:
Private Sub Command1_Click() Multi 2, 3, 4, 5, 6 End Sub
结果是720。
由于可变参数过程中的参数是Variant类型,因此,可以把任何类型的实参传送给该过程。例如,也不可用下面的代码调用上面个过程:
Private Sub Command1_Click() Dim a As Integer, b As Long, c As Variant, d As Integer a = 6: b = 8: c = 12: d = 2 Multi a, b, c, d End Sub
结果是1152。
例2:编写计算总和的sum函数(见教材P227)。
Dim x As Variant Dim y As Integer Dim intsum As Integer
Sub sum(ParamArray intnums()) For Each x In intnums y = y + x Next x intsum = y End Sub
Private Sub command1_click() sum 1, 3, 5, 7, 8 List1.AddItem
intsum '注意这里的变量intsum与“形参”数组名intnums()是两个完全不同的概念,你可以将变量intsum用其它名字代替,如兰色的intsum用z来代替。 End Sub
本例中,将子过程换为函数过程也一样。如将Sub换为Function。
|