TOP

VBA-第 9 课。过程和函数

在任何编程语言中都存在过程和函数等概念。它们还包含在 Excel 的内置编程语言 VBA 中。

  • 函数是返回结果的例程。函数调用是一个表达式,可以在其他表达式中或在赋值语句的右侧使用(在 VBA 中表示为“Function”);
  • 过程是不是函数的任何例程。在 VBA 中标记为“Sub”(来自“子例程”一词)。

  • 程序(Public - Private)

    此时,我们创建的所有过程都是 Public 类型,这意味着它们可以从任何模块访问。

     Sub example()
     
     '相同:
     Public Sub example()
    

    要使该过程仅在某个模块中可用,请使用 Private 关键字:

    Private Sub example()
    

    从另一个过程中间运行一个过程

    要从另一个过程中执行一个过程,只需输入其名称即可。

    这是一个非常简单的例子:

    Private Sub warning()
        MsgBox "Caution !!!" '“警告!”
    End Sub
    
    Sub macro_test()
        If Range("A1") = "" Then
            warning '=> 执行程序“warning”
        End If
        'ETC...
    End Sub
    

    论点

    参数使得可以在子过程中使用过程中的值(请记住,默认情况下,变量只能从声明它们的过程中使用)。

    Private Sub warning(var_text As String)
        MsgBox "Caution : " & var_text & " !"
    End Sub
    
    Sub macro_test()
        If Range("A1") = "" Then
            warning "empty cell"               '“空单元格”
        ElseIf Not IsNumeric(Range("A1")) Then
            warning "non-numerical value"      '“非数值”
        End If
    End Sub
    

    一个参数已添加到过程“warning”中,在本例中,它是“String”类型的变量“var_text”(功能区):

    Private Sub warning(var_text As String)
    

    该例程需要一个参数,因此我们需要在“warning”后面放置一个值来执行它:

    warning "empty cell"
    

    当我们想写多个参数时,它们应该用逗号分隔。

    可选参数

    默认情况下,如果过程有参数,则必须提供它们,如果不提供,则过程将不会被执行。

    可以使用Optional 关键字在强制参数之后添加可选参数。例子:

    Private Sub dialog_boxes(last_name As String, Optional first_name, Optional age)
    

    现在可以使用或不使用可选参数来执行此过程,如下所示:

     '示例1:显示姓氏:
     dialog_boxes last_name1
       
     '示例 2:我们显示姓氏和名字:
     dialog_boxes last_name1, first_name1
       
     '示例3:显示姓氏和年龄:
     dialog_boxes last_name1, , age1
       
     '示例 4:显示姓氏、名字和年龄:
     dialog_boxes last_name1, first_name1, age1
    

    参数必须按正确的顺序输入。

    为了测试过程中是否存在可选参数,我们使用 IsMissing 函数。此函数仅与某些函数类型(类型为 Variant)兼容,这一点至关重要,因为未声明可选参数的类型(未声明类型 = Variant)。

    这是使用上面讨论的两个代码片段的示例:

    Sub macro_test()
    
        Dim last_name1 As String, first_name1 As String, age1 As Integer
       
        last_name1 = Range("A1")
        first_name1 = Range("B1")
        age1 = Range("C1")
    
        '示例1:显示姓氏:
        dialog_boxes last_name1
       
        '示例 2:我们显示姓氏和名字:
        dialog_boxes last_name1, first_name1
       
        '示例3:显示姓氏和年龄:
        dialog_boxes last_name1, , age1
       
        '示例 4:显示姓氏、名字和年龄:
        dialog_boxes last_name1, first_name1, age1
    
    End Sub
    
    Private Sub dialog_boxes(last_name As String, Optional first_name, Optional age)
       
        If IsMissing(age) Then '如果年龄变量缺失...
           
            If IsMissing(first_name) Then '如果变量first_name丢失,那么
            							  '仅显示姓氏
               MsgBox last_name
            Else '否则,将显示姓氏和名字
               MsgBox last_name & " " & first_name
            End If
           
        Else '如果年龄变量存在...
    
            If IsMissing(first_name) Then '如果变量first_name丢失,那么
                                          '将显示姓氏和年龄
               MsgBox last_name & ", " & age & " years old"
            Else '否则,将显示姓氏、名字和年龄
               MsgBox last_name & " " & first_name & ", " & age & " years old"
            End If
       
        End If
           
    End Sub
    

    看下图(例1):

    ByRef - ByVal

    默认情况下,参数的类型为 ByRef,这意味着如果将变量作为参数传递,则也会传递对其的引用。换句话说,如果一个变量已被另一个子过程更改,那么调用该子过程的外部过程也会更改该变量。

    例子:

    Sub macro_test()
        Dim var_number As Integer
        
        var_number = 30
        calcul_square var_number
       
        MsgBox var_number
    End Sub
    
    Private Sub calcul_square(ByRef var_value As Integer) 'ByRef 是可选的
                                                          '(是默认值)
        var_value = var_value * var_value
    End Sub
    

    为了清楚起见,下面是宏开始执行时会发生什么的示例:

     var_number = 30
     '变量“var_number”的初始值为30
    
     calcul_square var_number
     '子过程以“var_number”作为参数运行
    
     Private Sub calcul_square(ByRef var_value As Integer)
     '变量“var_value”在某种程度上用于快速访问变量“var_number”,
     '这意味着如果变量“var_value”更改,变量“var_number”也会更改
     '(而且他们不必有相同的名字)
     var_value = var_value * var_value
     '变量“var_value”的值被更改(因此“var_number”也同时更改)
    
     End Sub
     '子程序结束
    
     MsgBox var_number
     '变量“var_number”已更改,因此对话框中现在将显示 900
    

    第二种方法是使用ByVal。

    与传递引用(标签)的 ByRef 不同,ByVal 传递一个值,这意味着作为参数传递的值尚未被修改。

    下面您可以看到前面的代码和 ByVal 是如何工作的:

     var_number = 30
     '变量“var_number”的初始值为30
    
     calcul_square var_number
     '子过程以“var_number”作为参数运行
    
     Private Sub calcul_square(ByVal var_value As Integer)
     '变量“var_value”复制变量“var_number”的值(这两个变量不相关)
    
     var_value = var_value * var_value
     '变量“var_value”的值已更改
    
     End Sub
     '子过程结束(在此示例中,子过程对任何内容都没有影响)
    
     MsgBox var_number
     '变量“var_number”尚未更改,因此对话框中将显示 30
    

    您需要记住的是:当不应更改变量时,请使用 ByVal。

    功能

    过程和函数之间的主要区别在于函数返回一个值。

    这是一个简单的例子:

    Function square(var_number)
        square = var_number ^ 2 '“square”函数返回“平方根”值
    End Function
    
    Sub macro_test()
        Dim result As Double
        result = square(9.876) '结果变量被赋予函数计算的值
        MsgBox result '显示结果(在本例中为 9.876 的平方)
    End Sub
    

    该函数可以像 Excel 中的任何其他函数一样在工作表上使用。

    例如,要获取单元格 A1 中输入的值的平方: