ZetCode

Visual Basic 中的过程和函数

最后修改于 2023 年 10 月 18 日

在本教程中,您将学习 Visual Basic 过程和函数。

我们使用过程和函数来创建模块化程序。Visual Basic 语句被分组在一个由 SubFunction 和匹配的 End 语句包围的块中。两者之间的区别在于函数返回数值,而过程不返回。

过程和函数是大型程序中的一段代码。它们执行特定的任务。使用过程和函数的优点是

过程

过程是 SubEnd Sub 语句内部的 Visual Basic 语句块。过程不返回值。

Program.vb
Option Strict On

Module Example

    Sub Main()

        SimpleProcedure()

    End Sub

    Sub SimpleProcedure()
        Console.WriteLine("Simple procedure")
    End Sub

End Module

此示例显示了过程的基本用法。在我们的程序中,我们有两个过程。 Main 过程和用户定义的 SimpleProcedure。正如我们已经知道的,Main 过程是 Visual Basic 程序的入口点。

SimpleProcedure()

每个过程都有一个名称。在 Main 过程内部,我们调用我们用户定义的 SimpleProcedure 过程。

Sub SimpleProcedure()

    Console.WriteLine("Simple procedure")
    
End Sub

过程是在 Main 过程之外定义的。过程名称紧跟在 Sub 语句之后。当我们在 Visual Basic 程序内部调用一个过程时,控制权将被交给该过程。过程块内的语句将被执行。

过程可以接受可选参数。

Program.vb
Option Strict On

Module Example

    Sub Main()

        Dim x As Integer = 55
        Dim y As Integer = 32

        Addition(x, y)

    End Sub

    Sub Addition(ByVal k As Integer, ByVal l As Integer)

        Console.WriteLine(k+l)

    End Sub

End Module

在上面的例子中,我们向 Addition 过程传递一些值。

Addition(x, y)

这里我们调用 Addition 过程并向其传递两个参数。这些参数是两个 Integer 值。

Sub Addition(ByVal k As Integer, ByVal l As Integer)

    Console.WriteLine(k+l)

End Sub

我们定义一个过程签名。过程签名是描述可以对函数进行合法调用的参数及其参数类型的方式。它包含过程的名称、它的参数及其类型,以及在函数的情况下还包含返回值。ByVal 关键字指定我们如何将值传递给过程。在我们的例子中,该过程获得两个数值,55 和 32。这些数字被相加,结果被打印到控制台。

函数

函数是 FunctionEnd Function 语句内部的 Visual Basic 语句块。函数返回值。

函数有两种基本类型。内置函数和用户定义函数。内置函数是 Visual Basic 语言的一部分。有各种数学、字符串或转换函数。

Program.vb
Option Strict On

Module Example

    Sub Main()

        Console.WriteLine(Math.Abs(-23))
        Console.WriteLine(Math.Round(34.56))
        Console.WriteLine($"ZetCode has {Len("Zetcode")} characters")

    End Sub

End Module

在前面的示例中,我们使用了两个数学函数和一个字符串函数。内置函数帮助程序员完成一些常见任务。

在下面的例子中,我们有一个用户定义的函数。

Program.vb
Option Strict On

Module Example

    Dim x As Integer = 55
    Dim y As Integer = 32

    Dim result As Integer

    Sub Main()

        result = Addition(x, y)
        Console.WriteLine(Addition(x, y))

    End Sub

    Function Addition(ByVal k As Integer, ByVal l As Integer) As Integer

        Return k+l

    End Function

End Module

将两个值传递给函数。我们将这两个值相加并将结果返回给 Main 函数。

result = Addition(x, y)

调用 Addition 函数。该函数返回一个结果,并将此结果分配给结果变量。

Function Addition(ByVal k As Integer, ByVal l As Integer) As Integer

    Return k+l

End Function

这是 Addition 函数签名及其主体。它还包括一个返回数据类型,用于返回值。在我们的例子中,它是一个 Integer。值使用 Return 关键字返回给调用者。

通过值传递参数,通过引用传递参数

Visual Basic 支持两种将参数传递给函数的方式。通过值和通过引用。为此,我们有两个关键字。ByValByRef。当我们将参数通过值传递时,函数仅使用值的副本进行操作。当我们处理大量数据时,这可能会导致性能开销。

当我们将值通过引用传递时,函数会接收到对实际值的引用。当修改时,原始值会受到影响。这种传递值的方式更节省时间和空间。另一方面,它更容易出错。

我们应该使用哪种参数传递方式?这取决于具体情况。假设我们有一组数据,员工的工资。如果我们想计算数据的某些统计数据,我们不需要修改它们。我们通过值传递。如果我们处理大量数据并且计算速度至关重要,我们通过引用传递。如果我们想修改数据,例如对工资进行一些减少或加薪,我们可能会通过引用传递。

以下两个示例涵盖了这两个概念。

Program.vb
Option Strict On

Module Example

    Dim a As Integer = 4
    Dim b As Integer = 7

    Sub Main()

        Console.WriteLine("Outside Swap procedure")
        Console.WriteLine($"a is {a}")
        Console.WriteLine($"b is {b}")

        Swap(a, b)

        Console.WriteLine("Outside Swap procedure")
        Console.WriteLine($"a is {a}")
        Console.WriteLine($"b is {b}")

    End Sub


    Sub Swap(ByRef a As Integer, ByRef b As Integer)

        Dim temp As Integer
        temp = a
        a = b
        b = temp

        Console.WriteLine("Inside Swap procedure")
        Console.WriteLine($"a is {a}")
        Console.WriteLine($"b is {b}")

    End Sub

End Module

Swap 过程交换 ab 变量之间的数字。原始变量不受影响。

Dim a As Integer = 4
Dim b As Integer = 7

一开始,这两个变量被初始化。

Swap(a, b)

我们调用 Swap 过程。该过程将 ab 变量作为参数。

temp = a
a = b
b = temp

Swap 过程内部,我们更改这些值。请注意,ab 变量是在本地定义的。它们仅在 Swap 过程内部有效。

$ dotnet run
Outside Swap procedure
a is 4
b is 7
Inside Swap procedure
a is 7
b is 4
Outside Swap procedure
a is 4
b is 7

下一个代码示例通过引用将值传递给函数。原始变量在 Swap 过程内部被更改。

Program.vb
Option Strict On

Module Example

    Dim a As Integer = 4
    Dim b As Integer = 7

    Sub Main()

        Console.WriteLine("Outside Swap procedure")
        Console.WriteLine($"a is {a}")
        Console.WriteLine($"b is {b}")

        Swap(a, b)

        Console.WriteLine("Outside Swap procedure")
        Console.WriteLine($"a is {a}")
        Console.WriteLine($"b is {b}")

    End Sub


    Sub Swap(ByRef a As Integer, ByRef b As Integer)

        Dim temp As Integer
        temp = a
        a = b
        b = temp

        Console.WriteLine("Inside Swap procedure")
        Console.WriteLine($"a is {a}")
        Console.WriteLine($"b is {b}")

    End Sub

End Module

在这个例子中,调用 Swap 过程将改变原始值。

Sub Swap(ByRef a As Integer, ByRef b As Integer)
 ...
End Sub

现在我们使用 ByRef 关键字来表示我们通过引用传递参数。

$ dotnet run
Outside Swap procedure
a is 4
b is 7
Inside Swap procedure
a is 7
b is 4
Outside Swap procedure
a is 7
b is 4

在这里我们可以看到,Swap 过程确实改变了变量的值。

递归

递归,在数学和计算机科学中,是一种定义函数的方法,其中被定义的函数在其自身的定义中被应用。换句话说,递归函数调用自身来完成其工作。递归是一种广泛用于解决许多编程任务的方法。

一个典型的例子是阶乘的计算。

Program.vb
Option Strict On

Module Example

    Sub Main()

        Console.WriteLine(Factorial(4))
        Console.WriteLine(Factorial(10))

    End Sub

    Function Factorial(ByVal n As Integer) As Integer

        If n = 0 Then
            Return 1
        Else
            Return n * Factorial(n-1)
        End If

    End Function

End Module

在此代码示例中,我们计算两个数的阶乘。

Return n * Factorial(n-1)

在阶乘函数的主体内部,我们使用修改后的参数调用阶乘函数。该函数调用它自己。

$ dotnet run
24
3628800

模块范围,过程范围

范围是变量可以被引用的范围。在过程内部声明的变量具有过程范围。它仅在此特定过程中有效。在模块内部声明的变量具有模块范围。它在模块中的任何地方都有效。

Program.vb
Option Strict On

Module Example

    Dim a As Integer = 2

    Sub Main()

        Dim b As Integer = 3

        Console.WriteLine(a)
        SimpleProcedure()

    End Sub

    Sub SimpleProcedure()
        Console.WriteLine(a)
        ' Console.WriteLine(b)
    End Sub

End Module

在前面的例子中,我们声明了两个变量。变量 a 具有模块范围,变量 b 具有过程范围。

Dim a As Integer = 2

变量 a 在 Example 模块内部声明,在两个过程之外声明。它在两个过程中都有效。

Sub Main()
    Dim b As Integer = 3
...
End Sub

变量 b 在 Main 过程中声明。它仅在那里有效。它在第二个过程中无效。

Sub SimpleProcedure()
    Console.WriteLine(a)
    ' Console.WriteLine(b)
End Sub

打印 b 变量的语句被注释掉了。如果我们取消注释该行,该示例将无法编译。

Program.vb
Option Strict On

Module Example

    Dim a As Integer = 2

    Sub Main()
        Dim a As Integer = 3

        Console.WriteLine(a)

    End Sub

End Module

在前面的示例中,我们声明了一个在两个不同范围内具有相同名称的变量。它们不会冲突。在 Main 过程内部声明的变量会覆盖在模块范围内声明的变量。

$ dotnet run
3

静态变量

静态变量是一个已静态分配的变量,其生命周期跨越程序的整个运行。默认情况下,局部变量在其函数连续调用中不会保留其值。

Program.vb
Option Strict On

Module Example

    Sub Main()

        NonStatic()
        NonStatic()
        NonStatic()
        NonStatic()
        Console.WriteLine(NonStatic)

    End Sub

    Function NonStatic() As Integer

        Dim x As Integer = 0
        x += 1
        Return x

    End Function

End Module

在上面的例子中,我们有一个普通的、非静态的变量。每次调用该函数时,我们都会递增该变量。我们调用该函数 5 次。但是,对于函数的每次调用,都会初始化非静态变量。

$ dotnet run
1

在 5 次函数调用后,x 变量等于 1。

静态变量仅在首次调用函数时初始化一次。此后它们会保留其值。

Program.vb
Option Strict On

Module Example

    Sub Main()

        StaticFunction()
        StaticFunction()
        StaticFunction()
        StaticFunction()
        Console.WriteLine(StaticFunction)

    End Sub

    Function StaticFunction() As Integer

        Dim Static x As Integer = 0
        x += 1
        Return x

    End Function

End Module

在 5 次连续调用后,x 等于 5。

Dim Static x As Integer = 0

静态变量是用 Static 关键字创建的。

$ dotnet run
5

在本 Visual Basic 教程中,我们介绍了过程和函数。