# Visual Basic 中的过程&函数

> 原文： [https://zetcode.com/lang/visualbasic/procedures/](https://zetcode.com/lang/visualbasic/procedures/)

在本部分中，您将学习 Visual Basic 过程&函数。

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

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

*   减少代码重复
*   将复杂的问题分解成更简单的部分
*   提高代码的清晰度
*   重用代码
*   信息隐藏

## 过程

过程是`Sub`，`End Sub`语句内的 Visual Basic 语句块。 过程不返回值。

```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 程序的入口点。

```vb
SimpleProcedure()

```

每个过程都有一个名称。 在`Main()`过程中，我们调用了用户定义的`SimpleProcedure()`过程。

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

```

程序在`Main()`程序之外定义。 过程名称位于`Sub`语句之后。 当我们在 Visual Basic 程序中调用过程时，该过程将得到控制。 执行该过程块内的语句。

过程可以采用可选参数。

```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()`过程。

```vb
Addition(x, y)

```

在这里，我们将调用`Addition()`过程并将两个参数传递给它。 这些参数是两个`Integer`值。

```vb
Sub Addition(ByVal k As Integer, ByVal l As Integer)
    Console.WriteLine(k+l)
End Sub

```

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

## 函数

函数是`Function`，`End Function`语句内的 Visual Basic 语句块。 函数返回值。

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

```vb
Option Strict On

Module Example

    Sub Main()

        Console.WriteLine(Math.Abs(-23))
        Console.WriteLine(Math.Round(34.56))
        Console.WriteLine("ZetCode has {0} characters", _
            Len("ZetCode"))

    End Sub

End Module

```

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

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

```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()`函数。

```vb
result = Addition(x, y)

```

调用加法函数。 该函数返回结果，并将该结果分配给结果变量。

```vb
Function Addition(ByVal k As Integer, _
                    ByVal l As Integer) As Integer
    Return k+l
End Function

```

这是加法函数签名及其主体。 它还包括用于返回值的返回数据类型。 在我们的例子中是`Integer`。 使用`Return`关键字将值返回给调用方。

## 按值传递参数，按引用传递

Visual Basic 支持将参数传递给函数的两种方法。 按值和引用。 为此，我们有两个关键字。 `ByVal`和`ByRef`。 当我们按值传递参数时，该函数仅适用于值的副本。 当我们处理大量数据时，这可能会导致性能开销。

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

我们应该使用哪种方式传递参数？ 这取决于实际情况。 假设我们有一组数据，即员工工资。 如果我们要计算数据的某些统计信息，则无需修改它们。 我们通过值。 如果我们处理大量数据，并且计算速度至关重要，则可以引用。 如果我们要修改数据，例如进行一些减薪或加薪，我们可以引用一下。

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

```vb
Option Strict On

Module Example

    Dim a As Byte = 4
    Dim b As Byte = 7

    Sub Main()

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

        Swap(a, b)

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

    End Sub

    Sub Swap(ByVal a As Byte, ByVal b As Byte) 
        Dim temp As Byte
        temp = a
        a = b
        b = temp
        Console.WriteLine("Inside Swap procedure")
        Console.WriteLine("a is {0}", a)
        Console.WriteLine("b is {0}", b)
    End Sub

End Module

```

`Swap()`过程在`a`和`b`变量之间交换数字。 原始变量不受影响。

```vb
Dim a As Byte = 4
Dim b As Byte = 7

```

最初，这两个变量被启动。

```vb
Swap(a, b)

```

我们称为`Swap()`过程。 该过程将`a`和`b`变量作为参数。

```vb
temp = a
a = b
b = temp

```

在`Swap()`过程中，我们更改了值。 请注意，`a`和`b`变量是在本地定义的。 它们仅在`Swap()`程序内有效。

```vb
$ ./swap1.exe 
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()`过程中更改。

```vb
Option Strict On

Module Example

    Dim a As Byte = 4
    Dim b As Byte = 7

    Sub Main()

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

        Swap(a, b)

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

    End Sub

    Sub Swap(ByRef a As Byte, ByRef b As Byte) 
        Dim temp As Byte
        temp = a
        a = b
        b = temp
        Console.WriteLine("Inside Swap procedure")
        Console.WriteLine("a is {0}", a)
        Console.WriteLine("b is {0}", b)
    End Sub

End Module

```

在此示例中，调用`Swap()`过程将更改原始值。

```vb
Sub Swap(ByRef a As Byte, ByRef b As Byte) 
 ...  
End Sub

```

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

```vb
$ ./swap2.exe 
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()`过程确实改变了变量的值。

## 递归

在数学和计算机科学中，递归是一种定义函数的方法，其中所定义的函数在其自己的定义内应用。 （维基百科）换句话说，递归函数调用自身以完成其工作。 递归是解决许多编程任务的一种广泛使用的方法。

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

```vb
Option Strict On

Module Example

    Sub Main()

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

    End Sub

    Function Factorial(ByVal n As UShort) As UShort
        If (n=0)
            Return 1
        Else
            Return n * Factorial(n-1)
        End If 

    End Function

End Module

```

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

```vb
Return n * Factorial(n-1)

```

在阶乘函数的主体内部，我们使用经过修改的参数调用阶乘函数。 该函数调用自身。

```vb
$ ./recursion.exe 
24
3628800

```

这些就是结果。

## 模块作用域，程序作用域

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

```vb
Option Strict On

Module Example

    Dim a As Byte = 2

    Sub Main()
        Dim b As Byte = 3         

        Console.WriteLine(a)
        SimpleProcedure()

    End Sub

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

End Module

```

在前面的示例中，我们声明了两个变量。 变量`a`具有模块作用域，变量`b`具有程序作用域。

```vb
Dim a As Byte = 2

```

变量`a`在两个过程之外的 Example 模块内部声明。 在两个过程中均有效。

```vb
Sub Main()
    Dim b As Byte = 3         
...
End Sub

```

变量`b`在`Main()`过程中声明。 仅在此有效。 在第二个过程中无效。

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

```

注释打印`b`变量的语句。 如果我们取消注释该行，则该示例将无法编译。

```vb
Option Strict On

Module Example

    Dim a As Byte = 2

    Sub Main()
        Dim a As Byte = 3         

        Console.WriteLine(a)

    End Sub

End Module

```

在前面的示例中，我们在两个不同的作用域中声明了一个具有相同名称的变量。 它们不会碰撞。 在`Main()`过程中声明的变量将覆盖在模块作用域中声明的变量。

```vb
$ ./scope2.exe 
3

```

输出。

## 静态变量

静态变量是已静态分配的变量，其生存期遍及整个程序运行。 （维基百科）默认的局部变量在函数的连续调用中不会保留其值。

```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 次。 但是，对于函数的每次调用都会启动非静态变量。

```vb
$ ./nonstatic.exe 
1

```

在 5 个函数调用之后，`x`变量等于 1。

首次调用函数时，静态变量仅启动一次。 之后，他们保留自己的值。

```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。

```vb
Dim Static x As Byte = 0

```

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

```vb
$ ./static.exe 
5

```

运行示例。

在 Visual Basic 教程的这一部分中，我们介绍了过程和函数。