Skip to content

2. VB.NET语言基础

2.1. 简介

我们首先将 VB.NET 视为一种经典的编程语言。关于对象的内容将在后续章节中讨论。

在程序中包含两类内容

  • 数据
  • 操作数据的指令

通常,我们力求将数据与指令分离:

2.2. VB.NET的数据

VB.NET 使用以下数据类型:

  1. 整数、实数和十进制数
  1. 字符和字符串
  2. 布尔值
  3. 日期
  4. 对象

2.2.1. 预定义的数据类型

类型 VB
等效类型 .NET
大小
值范围
布尔型
System.Boolean
2 字节
True 或 False。
字节
System.Byte
1 字节
0 至 255(无符号)。
字符
System.Char
2 字节
0 至 65 535(无符号)。
日期
System.DateTime
8 字节
0001年1月1日0:00:00至9999年12月31日23:59:59。
十进制
System.Decimal
16 字节
0 至 +/-79 228 162 514 264 337 593 543 950 335(无小数);0 至 +/-7.9228162514264337593543950335(28 位小数); 最小的非零数为 +/-0.0000000000000000000000000001 (+/-1E-28)。
双精度
System.Double
8字节
-1,79769313486231E+308 至
-4,94065645841247E-324 表示负值;4,94065645841247E-324 至 1,79769313486231E+308 表示正值。
整数
System.Int32
4字节
-2 147 483 648 至 2 147 483 647。
长整型
System.Int64
8字节
-9 223 372 036 854 775 808 至 9 223 372 036 854 775 807。
对象
System.Object
4字节
任何类型都可以存储在 Object 类型的变量中。
Short
System.Int16
2字节
-32 768 至 32 767。
Single
System.Single
4字节
-3,402823E+38 至 -1,401298E-45 用于负值;1,401298E-45 至 3,402823E+38 用于正值。
字符串
System.String(类)
 
0 到约 20 亿个 Unicode 字符。

在上表中,我们可以发现 32 位整数有两种可能的类型:IntegerSystem.Int32。 这两种类型可以互换。其他类型 VB 及其在 .NET 平台中的等效类型也是如此。以下是一个程序示例:


Module types
    Sub Main()
        ' 整数
        Dim var1 As Integer = 100
        Dim var2 As Long = 10000000000L
        Dim var3 As Byte = 100
        Dim var4 As Short = 4
        ' 实数
        Dim var5 As Decimal = 4.56789D
        Dim var6 As Double = 3.4
        Dim var7 As Single = -0.000103F
        ' 日期
        Dim var8 As Date = New Date(2003, 1, 1, 12, 8, 4)
        ' 布尔值
        Dim var9 As Boolean = True
        ' 字符
        Dim var10 As Char = "A"c
        ' 字符串
        Dim var11 As String = "abcde"
        ' 对象
        Dim var12 As Object = New Object
        ' 视图
        Console.Out.WriteLine("var1=" + var1.ToString)
        Console.Out.WriteLine("var2=" + var2.ToString)
        Console.Out.WriteLine("var3=" + var3.ToString)
        Console.Out.WriteLine("var4=" + var4.ToString)
        Console.Out.WriteLine("var5=" + var5.ToString)
        Console.Out.WriteLine("var6=" + var6.ToString)
        Console.Out.WriteLine("var7=" + var7.ToString)
        Console.Out.WriteLine("var8=" + var8.ToString)
        Console.Out.WriteLine("var9=" + var9.ToString)
        Console.Out.WriteLine("var10=" + var10)
        Console.Out.WriteLine("var11=" + var11)
        Console.Out.WriteLine("var12=" + var12.ToString)
    End Sub
End Module

运行结果如下:

var1=100
var2=10000000000
var3=100
var4=4
var5=4,56789
var6=3,4
var7=-0,000103
var8=01/01/2003 12:08:04
var9=True
var10=A
var11=abcde
var12=System.Object

2.2.2. 字面数据的标记

Integer
145, -7, &FF (十六进制)
Long
100000L
Double
134.789, -45E-18 (-45 × 10⁻¹⁸)
Single
134.789F, -45E-18F (-45 10-18)
Decimal
100000D
Char
"A"c
String
"今天"
Boolean
true, false
date
New Date(2003, 1, 1) 表示 2003年1月1日

请注意以下几点:

  • 100000L,字母 L 表示该数被视为长整型
  • 134.789F,字母 F 表示将该数视为单精度实数
  • 100000D,字母 D 表示将该数视为十进制实数
  • "A"c,用于将字符串 "A" 转换为字符 'A'
  • 字符串需用双引号 " " 包围。若字符串中需包含 " " 字符,则需将其重复一次,如 "abcd""e",以表示字符串 [abcd"e]。

2.2.3. 数据声明

2.2.3.1. 声明的作用

程序处理的数据由名称和类型来标识。这些数据存储在内存中。在程序编译时,编译器会根据程序员的声明,为每项数据分配一个由地址和大小标识的内存位置。 此外,这些声明还使编译器能够检测编程错误。例如,如果 x 是一个字符串,则运算 x=x*2 将被判定为错误。

2.2.3.2. 常量的声明

常量的声明语法如下:

const 标识符 as 类型=值

例如 [const PI as double=3.141592]。为何要声明常量?

  1. 如果为常量赋予一个有意义的名称,程序将更易于阅读:[const taux_tva as single=0.186F]
  2. 如果“常量”发生变化,修改程序也会更加容易。因此,在前面的例子中,如果增值税税率变为 33%,唯一需要修改的就是定义其值的语句:[const taux_tva as single=0.336F]。 如果程序中直接使用了0.186,那么就需要修改大量语句。

2.2.3.3. 变量声明

变量通过名称进行标识,并关联到一种数据类型。VB.NET 不区分大小写。因此,变量 FINfin 是相同的。变量可以在声明时进行初始化。声明一个或多个变量的语法为:

dim 变量1,变量2,...,变量n as 类型标识符

其中 identificateur_de_type 是一个预定义类型或由程序员定义的类型。

2.2.4. 数字与字符串之间的转换

nombre -> chaîne
nombre.ToString 或 "" & 数字 或 CType(数字,String)
objet -> chaîne
objet.ToString
chaine -> Integer
Integer.Parse(字符串) 或 Int32.Parse
chaîne -> Long
Long.Parse(字符串)或 Int64.Parse
chaîne -> Double
Double.Parse(字符串)
chaîne -> Single
Single.Parse(字符串)

如果字符串不代表一个有效的数字,则字符串到数字的转换可能会失败。 此时会引发一个名为 VB.NET 的致命错误(即异常)。可以通过以下 try/catch 子句来处理此错误:

try
        appel de la fonction susceptible de générer l'exception
 catch e as Exception
        traiter l'exception e
end try
instruction suivante

如果函数未引发异常,则执行下一条指令;否则,进入子句 catch 的主体,然后执行下一条指令。 我们稍后将详细讨论异常处理。以下是一个展示数字与字符串之间主要转换技术的程序。在此示例中,函数参数的值显示在屏幕上。因此,affiche(S) 会将 S 的值显示在屏幕上。


' 指令
Option Strict On

' 导入的命名空间
Imports System

' 测试模块
Module Module1

    Sub Main()
        ' 主过程
        ' 局部数据
        Dim S As String
        Const i As Integer = 10
        Const l As Long = 100000
        Const f As Single = 45.78F
        Dim d As Double = -14.98

        ' 数字 --> 字符串
        affiche(CType(i, String))
        affiche(CType(l, String))
        affiche(CType(f, String))
        affiche(CType(d, String))

        '布尔值 --> 字符串
        Const b As Boolean = False
        affiche(b.ToString)

        ' 字符串 --> 整数
        Dim i1 As Integer = Integer.Parse("10")
        affiche(i1.ToString)
        Try
            i1 = Integer.Parse("10.67")
            affiche(i1.ToString)
        Catch e As Exception
            affiche("Erreur [10.67] : " + e.Message)
        End Try

        ' 字符串 --> 长整型
        Dim l1 As Long = Long.Parse("100")
        affiche("" + l1.ToString)
        Try
            l1 = Long.Parse("10.675")
            affiche("" & l1)
        Catch e As Exception
            affiche("Erreur [10.675] : " + e.Message)
        End Try

        ' 字符串 --> 双精度
        Dim d1 As Double = Double.Parse("100,87")
        affiche(d1.ToString)
        Try
            d1 = Double.Parse("abcd")
            affiche("" & d1)
        Catch e As Exception
            affiche("Erreur [abcd] : " + e.Message)
        End Try

        ' 字符串 --> 单精度
        Dim f1 As Single = Single.Parse("100,87")
        affiche(f1.ToString)
        Try
            d1 = Single.Parse("abcd")
            affiche(f1.ToString)
        Catch e As Exception
            affiche("Erreur [abcd] : " + e.Message)
        End Try
    End Sub

    ' 显示
    Public Sub affiche(ByVal S As String)
        Console.Out.WriteLine("S=" + S)
    End Sub
End Module

所得结果如下:

S=10
S=100000
S=45,78
S=-14,98
S=False
S=10
S=Erreur [10.67] : Le format de la chaîne d'entrée est incorrect.
S=100
S=Erreur [10.675] : Le format de la chaîne d'entrée est incorrect.
S=100,87
S=Erreur [abcd] : Le format de la chaîne d'entrée est incorrect.
S=100,87
S=Erreur [abcd] : Le format de la chaîne d'entrée est incorrect.

需要注意的是,以字符串形式表示的实数必须使用逗号,而不是小数点。因此,应写为 Dim d As Double = -14.98,但 Dim d1 As Double = Double.Parse("100,87")

2.2.5. 数据数组

数组 VB.NET 是一种对象,用于将同类型的数据集合在同一个标识符下。其声明如下:

Dim Tableau(n) as type Dim Tableau() as type = New type(n) {}

其中 n 是数组最后一个元素的索引。语法 Tableau(i) 表示编号为 i 的数据,其中 i 属于区间 [0,n]。 任何引用数据 Tableau(i) 的操作,若 i 不属于区间 [0,n],都将引发异常。数组可以在声明时同时进行初始化。 在这种情况下,无需指定最后一个元素的编号。


        Dim entiers() As Integer = {0, 10, 20, 30}

数组有一个名为Length的属性,表示数组中的元素个数。以下是一个示例程序:


Module tab0
    Sub Main()
        ' 第一个数组
        Dim tab0(5) As Integer
        For i As Integer = 0 To UBound(tab0)
            tab0(i) = i
        Next
        For i As Integer = 0 To UBound(tab0)
            Console.Out.WriteLine("tab0(" + i.ToString + ")=" + tab0(i).tostring)
        Next

        ' 第二个数组
        Dim tab1() As Integer = New Integer(5) {}
        For i As Integer = 0 To tab1.Length - 1
            tab1(i) = i * 10
        Next
        For i As Integer = 0 To tab1.Length - 1
            Console.Out.WriteLine("tab1(" + i.ToString + ")=" + tab1(i).tostring)
        Next
    End Sub
End Module

及其运行结果:

tab0(0)=0
tab0(1)=1
tab0(2)=2
tab0(3)=3
tab0(4)=4
tab0(5)=5
tab1(0)=0
tab1(1)=10
tab1(2)=20
tab1(3)=30
tab1(4)=40
tab1(5)=50

一个二维数组可以声明如下:

Dim Tableau(n,m) as Type Dim Tableau(,) as Type = New Type(n,m) {}

其中 n+1 表示行数,m+1 表示列数。语法 Tableau(i,j) 表示 Tableau 中第 i 行第 j 个元素。 二维数组也可以在声明时同时初始化:


        Dim réels(,) As Double = {{0.5, 1.7}, {8.4, -6}}

每个维度的元素数量可通过方法 GetLenth(i) 获取,其中 i=0 表示对应第一个索引的维度,i=1 表示对应第二个索引的维度,……以下是一个示例程序:


Module Module2
    Sub Main()
        ' 第一个表格
        Dim tab0(2, 1) As Integer
        For i As Integer = 0 To UBound(tab0)
            For j As Integer = 0 To tab0.GetLength(1) - 1
                tab0(i, j) = i * 10 + j
            Next
        Next
        For i As Integer = 0 To UBound(tab0)
            For j As Integer = 0 To tab0.GetLength(1) - 1
                Console.Out.WriteLine("tab0(" + i.ToString + "," + j.ToString + ")=" + tab0(i, j).tostring)
            Next
        Next

        ' 第二个表格
        Dim tab1(,) As Integer = New Integer(2, 1) {}
        For i As Integer = 0 To tab1.GetLength(0) - 1
            For j As Integer = 0 To tab1.GetLength(1) - 1
                tab1(i, j) = i * 100 + j
            Next
        Next
        For i As Integer = 0 To tab1.GetLength(0) - 1
            For j As Integer = 0 To tab1.GetLength(1) - 1
                Console.Out.WriteLine("tab1(" + i.ToString + "," + j.ToString + ")=" + tab1(i, j).tostring)
            Next
        Next
    End Sub
End Module

及其执行结果:

tab0(0)=0
tab0(1)=1
tab0(2)=2
tab0(3)=3
tab0(4)=4
tab0(5)=5
tab1(0)=0
tab1(1)=10
tab1(2)=20
tab1(3)=30
tab1(4)=40
tab1(5)=50

数组的数组声明如下:

Dim Tableau(n)() as Type Dim Tableau()() as Type = new Type(n)()

上述声明创建了一个包含 n+1 行 的数组。每个 Tableau(i) 元素都是一个一维数组的引用。上述声明时不会创建这些数组。以下示例演示了如何创建数组数组:


        ' 一个数组数组
        Dim noms()() As String = New String(3)() {}
        ' 初始化
        For i = 0 To noms.Length - 1
            noms(i) = New String(i) {}
            For j = 0 To noms(i).Length - 1
                noms(i)(j) = "nom" & i & j
            Next
        Next

这里 noms(i) 是一个包含 i+1 个元素的数组。由于 noms(i) 是一个数组,因此 noms(i).Length 即为其元素个数。以下是一个将我们刚刚介绍的三种数组类型结合起来的示例:


' 指令
Option Strict On
Option Explicit On 

' 导入
Imports System

' 测试类
Module test
    Sub main()
        ' 一个已初始化的一维数组
        Dim entiers() As Integer = {0, 10, 20, 30}
        Dim i As Integer
        For i = 0 To entiers.Length - 1
            Console.Out.WriteLine("entiers[" & i & "]=" & entiers(i))
        Next

        ' 一个已初始化的二维数组
        Dim réels(,) As Double = {{0.5, 1.7}, {8.4, -6}}
        Dim j As Integer
        For i = 0 To réels.GetLength(0) - 1
            For j = 0 To réels.GetLength(1) - 1
                Console.Out.WriteLine("réels[" & i & "," & j & "]=" & réels(i, j))
            Next
        Next

        ' 数组数组
        Dim noms()() As String = New String(3)() {}

        ' 初始化
        For i = 0 To noms.Length°- 1
            noms(i) =°New String(i) {}
            For j = 0 To noms(i).Length - 1
                noms(i)(j) = "nom" & i & j
            Next
        Next

        ' 显示
        For i = 0 To noms.Length°- 1
            For j = 0 To noms(i).Length - 1
                Console.Out.WriteLine("noms[" & i & "][" & j & "]=" & noms(i)(j))
            Next
        Next
    End Sub
End Module

运行后,我们得到以下结果:

entiers[0]=0
entiers[1]=10
entiers[2]=20
entiers[3]=30
réels[0,0]=0,5
réels[0,1]=1,7
réels[1,0]=8,4
réels[1,1]=-6
noms[0][0]=nom00
noms[1][0]=nom10
noms[1][1]=nom11
noms[2][0]=nom20
noms[2][1]=nom21
noms[2][2]=nom22
noms[3][0]=nom30
noms[3][1]=nom31
noms[3][2]=nom32
noms[3][3]=nom33

2.3. VB.NET的基本说明

区分

1 由计算机执行的基本指令。

2 控制程序运行的指令。

当我们考察微型计算机及其外围设备的结构时,基本指令便显而易见。

  1. 读取来自键盘的信息

  2. 信息处理

  3. 将信息写入屏幕

  4. 从磁盘文件读取信息

  5. 将信息写入磁盘文件

2.3.1. 屏幕书写

屏幕上存在多种书写指令:

Console.Out.WriteLine(expression)
Console.WriteLine(expression)
Console.Error.WriteLine (expression)

其中 expression 代表任何可转换为字符串并在屏幕上显示的数据类型。在迄今为止的示例中,我们仅使用了 Console.Out.WriteLine(表达式) 语句。

System.Console 提供了屏幕写入操作(WriteWriteLine)。类 Console 具有两个属性 Out 和 Error,它们是类型为 StreamWriter 的写入流

  • Console.WriteLine()Console.Out.WriteLine() 功能等同,并向通常与屏幕关联的 Out 流写入数据。
  • Console.Error.WriteLine() 写入 Error 流,该流通常也关联到屏幕。

OutError 流默认关联到屏幕。但正如我们稍后将看到的,它们可以在程序运行时重定向到文本文件。

2.3.2. 读取键盘输入的数据

来自键盘的数据流由类型为 StreamReader 的对象 Console.In 表示。此类对象可通过 ReadLine 方法读取一行文本:


        Dim ligne As String = Console.In.ReadLine()

键盘输入的行被存储在变量 ligne 中,随后可由 programme.Le 进行处理。In 流可像 OutError 流一样重定向到文件中。

2.3.3. 输入输出示例

以下是一个简短的程序,用于演示键盘/屏幕的输入输出操作:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

' 模块
Module io1
    Sub Main()
        ' 写入 Out 流
        Dim obj As New Object
        Console.Out.WriteLine(("" & obj.ToString))

        ' 写入 Error 流
        Dim i As Integer = 10
        Console.Error.WriteLine(("i=" & i))

        ' 读取键盘输入的一行
        Console.Out.Write("Tapez une ligne : ")
        Dim ligne As String = Console.In.ReadLine()
        Console.Out.WriteLine(("ligne=" + ligne))
    End Sub
End Module

以及运行结果:

System.Object
i=10
Tapez une ligne : ceci est un essai
ligne=ceci est un essai

指令


        Dim obj As New Object
        Console.Out.WriteLine(obj.ToString)

仅用于说明任何对象均可被显示。本文不打算解释所显示内容的含义。

2.3.4. I/O重定向

在 DOS/Windows 系统中,有三个标准设备,分别称为:

  1. 标准输入设备——默认指键盘,编号为 0
  2. 标准输出设备——默认指向屏幕,编号为 1
  3. 标准错误设备——默认指向屏幕,编号为 2

在 VB.NET 中, 写入流 Console.Out 写入设备 1,写入流 Console.Error 写入设备 2,而读取流 Console.In 则从设备 0 读取数据。 在 Windows 的 DOS 窗口中运行程序时,可以指定该程序所使用的设备 0、1 和 2。请看以下命令行:

pg arg1 arg2 .. argn

在程序 pg 的参数 argi 之后,可以将标准 I/O 设备重定向到文件:

0<in.txt
标准输入流 0 被重定向到文件 in.txt。因此,在程序中,流 Console.In 将从文件 in.txt 中获取数据。
  
1>out.txt
将输出流 1 重定向至文件 out.txt。这导致在程序中,数据流 Console.Out 会将数据写入文件 out.txt
   
1>>out.txt
同上,但写入的数据将添加到文件 out.txt 的当前内容中。
   
2>error.txt
将输出流 2 重定向至文件 error.txt。这将导致程序中的数据流 Console.Error 将其数据写入文件 error.txt
   
2>>error.txt
同上,但写入的数据将追加到文件 error.txt 的现有内容中。
   
1>out.txt 2>error.txt
设备 1 和 2 均被重定向到文件
   

需要注意的是,为了将程序 pg 的 I/O 流重定向到文件,无需修改程序 pg。 OS 程序负责确定设备 0、1 和 2 的性质。请看以下程序:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

' 重定向
Module console2
    Sub Main()
        ' 读取 In 流
        Dim data As String = Console.In.ReadLine()
        ' 写入 Out 流
        Console.Out.WriteLine(("écriture dans flux Out : " + data))
        ' 写入 Error 流
        Console.Error.WriteLine(("écriture dans flux Error : " + data))
    End Sub
End Module

编译该程序:

dos>vbc es2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
Copyright (C) Microsoft Corporation 1987-2002. Tous droits réservés.

dos>dir
24/02/2004  15:39                  416 es2.vb
11/03/2004  08:20                3 584 es2.exe

让我们进行首次运行:

dos>es2.exe
un premier test
écriture dans flux Out : un premier test
écriture dans flux Error : un premier test

上一次执行并未重定向任何标准 I/O 流 InOutError。现在我们将重定向这三个流。流 In 将被重定向到文件 in.txt, Out 流将重定向到文件 out.txt,Error 流将重定向到文件 error.txt。此重定向在命令行中以以下形式进行

dos>es2.exe 0<in.txt 1>out.txt 2>error.txt

执行后得到以下结果:

dos>more in.txt
un second test

dos>es2.exe 0<in.txt 1>out.txt 2>error.txt

dos>more out.txt
écriture dans flux Out : un second test

dos>more error.txt
écriture dans flux Error : un second test

可以清楚地看到,流程 OutError 写入的设备并不相同。

2.3.5. 将表达式的值赋给变量

这里我们关注的是变量=表达式这一操作。表达式可以是以下类型:算术、关系、布尔、字符。

2.3.5.1. 运算符列表

操作
语言元素
Arithmétique
^, –, *, /, \, Mod, +, =
Assignation
=, ^=, *=, /=, \=, +=, -=, &=
Comparaison
=, <>, <, >, <=, >=, Like, Is
Concaténation
&, +
Opérations logiques/de bits
非、与、或、异或、AndAlso、OrElse
Opérations diverses
AddressOf, GetType

2.3.5.2. 算术表达式

算术表达式的运算符如下:

Arithmétique
^, –, *, /, \, Mod, +, =

+:加法,-:减法,*:乘法,/:实数除法,\:整数除法商,Mod:整数除法余数,^:幂运算。因此,以下程序:


' 算术运算符
Module operateursarithmetiques
    Sub Main()
        Dim i, j As Integer
        i = 4 : j = 3
        Console.Out.WriteLine(i & "/" & j & "=" & (i / j))
        Console.Out.WriteLine(i & "\" & j & "=" & (i \ j))
        Console.Out.WriteLine(i & " mod " & j & "=" & (i Mod j))

        Dim r1, r2 As Double
        r1 = 4.1 : r2 = 3.6
        Console.Out.WriteLine(r1 & "/" & r2 & "=" & (r1 / r2))
        Console.Out.WriteLine(r1 & "^2=" & (r1 ^ 2))
        Console.Out.WriteLine(Math.Cos(3))
    End Sub
End Module

将产生以下结果:

4/3=1,33333333333333
4\3=1
4 mod 3=1
4,1/3,6=1,13888888888889
4,1^2=16,81
-0,989992496600445

数学函数种类繁多。以下列举其中几种:


Public Shared Function Sqrt(ByVal d As Double ) As Double
平方根

[Visual Basic]
Public Shared Function Cos(ByVal d As Double ) As Double
余弦

Public Shared Function Sin(ByVal a As Double) As Double
正弦

[Visual Basic]
Public Shared Function Tan(ByVal a As Double) As Double
正切

[Visual Basic]
Public Shared Function Pow(ByVal x As Double,ByVal y As Double) As Double
x的y次方 (x>0)

[Visual Basic]
Public Shared Function Exp(ByVal d As Double) As Double
指数函数

[Visual Basic]
Overloads Public Shared Function Log( ByVal d As Double ) As Double
自然对数

Overloads Public Shared Function Abs(ByVal value As Double ) As Double
绝对值
....
 

所有这些函数都定义在一个名为 Math 的类中。使用时,必须在函数名前加上其所属类的名称。因此,应写为:

        Dim r1, r2 As Double
        r2 = Math.Sqrt(9)
        r1 = Math.Cos(3)

Math 的完整定义如下:

E
表示由常数 e 指定的自然对数底。
   
PI
表示圆周长与直径之比,由常数 π 指定。
   
Abs
重载。返回指定数字的绝对值。
  
Acos
返回其余弦值为指定数的角。
  
Asin
返回正弦值为指定数的角。
  
Atan
返回正切值为指定数值的角。
  
Atan2
返回正切值为两个指定数字之商的角度。
  
BigMul
计算两个 32 位数的整数乘积。
  
Ceiling
返回不小于指定数的最小整数。
  
Cos
返回指定角度的余弦值。
  
Cosh
返回指定角度的双曲余弦值。
  
DivRem
重载。返回两个数的商,并将余数作为输出参数返回。
  
Exp
返回 e 的指定次幂。
  
Floor
返回不大于指定数的最大整数。
  
IEEERemainder
返回指定数除以另一个数的余数。
  
Log
已重载。返回指定数的对数。
  
Log10
返回指定数的十进制对数。
  
Max
重载。返回两个指定数字中的较大者。
  
Min
重载。返回两个数中的较小值。
  
Pow
返回指定数字的指定次方。
  
Round
重载。返回最接近指定值的数字。
  
Sign
重载。返回一个表示数字符号的值。
  
Sin
返回指定角度的正弦值。
  
Sinh
返回指定角度的双曲正弦值。
  
Sqrt
返回指定数的平方根。
  
Tan
返回指定角度的正切值。
  
Tanh
返回指定角度的双曲正切值。
  

当一个函数被声明为“重载”时,意味着它针对不同类型的参数存在。例如,函数 Abs(x) 针对 x 的 Integer、Long、Decimal、Single 和 Float 类型分别存在。对于每种类型,都有一个独立的 Abs 函数定义。因此,我们说该函数是重载的。

2.3.5.3. 算术表达式求值中的优先级

在求算算术表达式时,运算符的优先级如下(从最高优先级到最低优先级):

类别
运算符
Primaire
所有不含运算符的表达式:函数、括号
Élévation à la puissance
^
Négation unaire
+, -
Multiplication
*, /
Division par un entier
\
Modulo
Mod
Addition
+, -

2.3.5.4. 关系运算符

运算符如下:

Comparaison
=, <>, <, >, <=, >=, Like, Is

=:等于,<>:不等于,<:小于(严格),>:大于(严格),<=:小于或等于,>=:大于或等于,Like:符合模式,Is:对象相等。所有这些运算符具有相同的优先级。它们从左到右进行求值。关系表达式的结果为布尔值。

字符串比较:考虑以下程序:


' 命名空间
Imports System

Module string1
    Sub main()
        Dim ch1 As Char = "A"c
        Dim ch2 As Char = "B"c
        Dim ch3 As Char = "a"c
        Console.Out.WriteLine("A<B=" & (ch1 < ch2))
        Console.Out.WriteLine("A<a=" & (ch1 < ch3))
        Dim chat As String = "chat"
        Dim chien As String = "chien"
        Dim chaton As String = "chaton"
        Dim chat2 As String = "CHAT"
        Console.Out.WriteLine("chat<chien=" & (chat < chien))
        Console.Out.WriteLine("chat<chaton=" & (chat < chaton))
        Console.Out.WriteLine("chat<CHAT=" & (chat < chat2))
        Console.Out.WriteLine("chaton like chat*=" & ("chaton" Like "chat*"))
    End Sub
End Module

及其执行结果:

A<B=True
A<a=True
chat<chien=True
chat<chaton=True
chat<CHAT=False
chaton like chat*=True

假设有两个字符 C1 和 C2。可以使用以下运算符对它们进行比较:<、<=、=、<>、>、>=。此时比较的是它们的 Unicode 字符值,即数字。 根据Unicode排序规则,它们之间的关系如下:

空格 < .. < '0' < '1' < .. < '9' < .. < 'A' < 'B' < .. < 'Z' < .. < 'a' < 'b' < .. < 'z'

字符串按字符逐个比较。两个字符之间遇到的第一个不等关系,将导致字符串之间产生同向的不等关系。基于上述说明,请读者研究前一个程序的运行结果。

2.3.5.5. 布尔表达式

运算符如下:

Opérations logiques/de bits
Not、And、Or、Xor、AndAlso、OrElse

Not:逻辑与,Or:逻辑或,Not:否定,Xor:异或。

op1 AndAlso op2:如果 op1 为假,则不计算 op2,结果为假。

op1 OrElse op2:如果 op1 为真,则不计算 op2,结果为真。

这些运算符之间的优先级如下:

NOT logique
AND logique
And, AndAlso
OR logique
或者,OrElse
XOR logique
异或

布尔表达式的结果是一个布尔值。

2.3.5.6. 位运算

一方面,这里包含与布尔运算符相同的运算符,且优先级相同。此外还有两个移位运算符:<< 和 >>。设 i 和 j 为两个整数。

i<<n
将 i 向左移 n 位。新移入的位均为零。
i>>n
将 i 向右移位 n 位。如果 i 是带符号整数(signed char、int、long),则保留符号位。
i & j
对 i 和 j 进行位与运算。
i | j
执行 i 与 j 的按位逻辑或运算。
~i
将 i 补为 1
i^j
对 i 和 j 执行 OU EXCLUSIF 运算

假设以下程序:


Module operationsbit
    Sub main()
        ' 位操作
        Dim i As Short = &H123F
        Dim k As Short = &H7123
        Console.Out.WriteLine("i<<4=" & (i << 4).ToString("X"))
        Console.Out.WriteLine("i>>4=" & (i >> 4).ToString("X"))
        Console.Out.WriteLine("k>>4=" & (k >> 4).ToString("X"))
        Console.Out.WriteLine("i and 4=" & (i And 4).ToString("X"))
        Console.Out.WriteLine("i or 4 =" & (i Or 4).ToString("X"))
        Console.Out.WriteLine("not i=" & (Not i).ToString("X"))
    End Sub
End Module

其执行结果如下:

i<<4=23F0
i>>4=123
k>>4=712
i and 4=4
i or k =123F
not i=EDC0

2.3.5.7. 与赋值相关的运算符

可以写成 a+=b,其含义为 a=a+b。可与赋值运算结合使用的运算符列表如下:

combinaison d'opérateurs
^=, *=, /=, \=, +=, -=, &=

2.3.5.8. 运算符的一般优先级

类别
操作员
Primaire
所有不含运算符的表达式
Élévation à la puissance
^
Négation unaire
+, -
Multiplication
*, /
Division par un entier
\
Modulo
Mod
Addition
+, -
Concaténation
&
Déplacement
<<, >>
Relationnel
=, <>, <, >, <=, >=, Like, Is, TypeOf...Is
NOT logique
Not
AND logique
And, AndAlso
OR logique
或者,OrElse
XOR logique
异或

当一个操作数位于两个优先级相同的运算符之间时,运算符的结合律决定了运算的执行顺序。所有运算符均为左结合,这意味着运算从左到右执行。优先级和结合律可以通过括号中的表达式来控制。

2.3.5.9. 类型转换

存在一些预定义函数,可用于将一种数据类型转换为另一种。其列表如下:

CBool,CByte,CChar,CDate,CDbl,CDec,CInt,CLng,CObj,CShort,CSng,CStr

这些函数接受数值表达式或字符串作为参数。结果的类型如下表所示:

函数
结果
函数参数的取值范围
CBool
布尔型
任何有效的字符串或数字表达式。
CByte
字节
0 至 255;小数部分将四舍五入。
CChar
字符
任何有效的字符串表达式;值可在 0 到 65 535 之间。
CDate
日期
任何有效的日期和时间表示形式。
CDbl
Double
-1,79769313486231E+308 至
-4,94065645841247E-324 表示负值;4,94065645841247E-324 至 1,79769313486231E+308 表示正值。
CDec
十进制
+/-79 228 162 514 264 337 593 543 950 335(无小数部分的数值)。28位小数的数值范围为
+/-7.9228162514264337593543950335。最小的非零数是 0.0000000000000000000000000001。
CInt
整数
-2 147 483 648 到 2 147 483 647;小数部分被四舍五入。
CLng
长整型
-9 223 372 036 854 775 808 至 9 223 372 036 854 775 807;小数部分四舍五入。

CObj
对象
任何有效的表达式。
CShort
Short
-32 768 至 32 767;小数部分四舍五入。
CSng
单精度
对于负值,范围为 -3,402823E 至 -1,401298E-45;对于正值,范围为 1,401298E-45 至 3,402823E+38。
CStr
字符串
Cstr 函数返回的值取决于表达式参数。

以下是一个示例程序:


Module conversion
    Sub main()
        Dim var1 As Boolean = CBool("true")
        Dim var2 As Byte = CByte("100")
        Dim var3 As Char = CChar("A")
        Dim var4 As Date = CDate("30 janvier 2004")
        Dim var5 As Double = CDbl("100,45")
        Dim var6 As Decimal = CDec("1000,67")
        Dim var7 As Integer = CInt("-30")
        Dim var8 As Long = CLng("456")
        Dim var9 As Short = CShort("-14")
        Dim var10 As Single = CSng("56,78")
        Console.Out.WriteLine("var1=" & var1)
        Console.Out.WriteLine("var2=" & var2)
        Console.Out.WriteLine("var3=" & var3)
        Console.Out.WriteLine("var4=" & var4)
        Console.Out.WriteLine("var5=" & var5)
        Console.Out.WriteLine("var6=" & var6)
        Console.Out.WriteLine("var7=" & var7)
        Console.Out.WriteLine("var8=" & var8)
        Console.Out.WriteLine("var9=" & var9)
        Console.Out.WriteLine("var10=" & var10)
    End Sub
End Module

及其执行结果:

var1=True
var2=100
var3=A
var4=30/01/2004
var5=100,45
var6=1000,67
var7=-30
var8=456
var9=-14
var10=56,78

也可以使用函数 CType(expression, type),如下面的程序所示:


Module ctype1
    Sub main()
        Dim var1 As Boolean = CType("true", Boolean)
        Dim var2 As Byte = CType("100", Byte)
        Dim var3 As Char = CType("A", Char)
        Dim var4 As Date = CType("30 janvier 2004", Date)
        Dim var5 As Double = CType("100,45", Double)
        Dim var6 As Decimal = CType("1000,67", Decimal)
        Dim var7 As Integer = CType("-30", Integer)
        Dim var8 As Long = CType("456", Long)
        Dim var9 As Short = CType("-14", Short)
        Dim var10 As Single = CType("56,78", Single)
        Dim var11 As String = CType("47,89", String)
        Dim var12 As String = 47.89.ToString
        Dim var13 As String = "" & 47.89
        Console.Out.WriteLine("var1=" & var1)
        Console.Out.WriteLine("var2=" & var2)
        Console.Out.WriteLine("var3=" & var3)
        Console.Out.WriteLine("var4=" & var4)
        Console.Out.WriteLine("var5=" & var5)
        Console.Out.WriteLine("var6=" & var6)
        Console.Out.WriteLine("var7=" & var7)
        Console.Out.WriteLine("var8=" & var8)
        Console.Out.WriteLine("var9=" & var9)
        Console.Out.WriteLine("var10=" & var10)
        Console.Out.WriteLine("var11=" & var11)
        Console.Out.WriteLine("var12=" & var12)
        Console.Out.WriteLine("var13=" & var13)
    End Sub
End Module

该程序将产生以下结果:

var1=True
var2=100
var3=A
var4=30/01/2004
var5=100,45
var6=1000,67
var7=-30
var8=456
var9=-14
var10=56,78
var11=47,89
var12=47,89
var13=47,89

2.4. 程序执行控制指令

2.4.1. 停止

在类 Environment 中定义的方法 Exit 可用于停止程序的执行:


[Visual Basic]
Public Shared Sub Exit(ByVal exitCode As Integer )

将终止当前进程,并向父进程返回值 exitCode。父进程可使用 exitCode 的值。 在 DOS 环境下,该状态变量会被返回为 DOS,并存储在系统变量 ERRORLEVEL 中,该值可在批处理文件中进行检测。 在 Unix 系统中,变量 $? 用于获取 exitCode 的值。

    Environment.Exit(0)

将以状态值为 0 终止程序的执行。

2.4.2. 简单选择结构

if condition then
    actions_alors
else
    actions_sinon
end if
  • 每项操作占一行
  • else子句可以省略。

可以选择结构进行嵌套,如下例所示:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

Module if1
    Sub main()
        Dim i As Integer = 10
        If i > 4 Then
            Console.Out.WriteLine(i & " est > " & 4)
        Else
            If i = 4 Then
                Console.Out.WriteLine(i & " est = " & 4)
            Else
                Console.Out.WriteLine(i & " est < " & 4)
            End If
        End If
    End Sub
End Module

所得结果:

10 est > 4

2.4.3. 案例结构

语法如下:

select case expression
    case liste_valeurs1
        actions1
case liste_valeurs2
        actions2
...
case else
        actions_sinon
end select
  • [expression] 的类型必须是以下类型之一:
Boolean, Byte, Char, Date, Decimal, Double, Integer, Long, Object, Short, Single et String
  • [case else]子句可以省略。
  • [liste_valeursi] 是该表达式的可能取值。[listes_valeursi] 表示条件列表 condition1, condition2, ..., conditionx。 如果 [expression] 验证了其中一个条件,则执行 [liste_valeursi] 子句后面的操作。条件可以采用以下形式:
  • val1 to val2:若 [expression] 属于 [val1,val2] 域,则为真
  • val1:若 [expression] 等于 val1,则为真
  • is > val1:若 [expression] > val1 则为真。关键字 [is] 可以省略
  • 运算符 =、<、<=、>、>=、<> 亦同
  • 仅执行与第一个满足条件相关的操作。

考虑以下程序:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

Module selectcase1
    Sub main()
        Dim i As Integer = 10
        Select Case i
            Case 1 To 4, 7 To 8
                Console.Out.WriteLine("i est dans l'intervalle [1,4] ou [7,8]")
            Case Is > 12
                Console.Out.WriteLine("i est > 12")
            Case Is < 15
                Console.Out.WriteLine("i est < 15")
            Case Is < 20
                Console.Out.WriteLine("i est < 20")
        End Select
    End Sub
End Module

它产生以下结果:

i est < 15

2.4.4. 重复结构

2.4.4.1. 已知重复次数


For counter [ As datatype ] = start To end [ Step step ]
   actions
Next [ counter ]

对于变量 [counter] 所取的每个值,都会执行相应的操作。请看以下程序:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

Module for1
    Sub main()
        Dim somme As Integer = 0
        Dim résultat As String = "somme("
        For i As Integer = 0 To 10 Step 2
            somme += i
            résultat += " " + i.ToString
        Next
        résultat += ")=" + somme.ToString
        Console.Out.WriteLine(résultat)
    End Sub
End Module

结果:

somme( 0 2 4 6 8 10)=30

另一种迭代次数已知的迭代结构如下:


For Each element [ As datatype ] In groupe
   [ actions ]
Next [ element ]
  • groupe 是一个对象集合。我们已经知道的对象集合是数组
  • datatype 是集合中对象的类型。对于数组而言,这相当于数组元素的类型
  • element 是循环的局部变量,它将依次取集合中的所有值作为其值。

因此,以下代码:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

Module foreach1
    Sub main()
        Dim amis() As String = {"paul", "hélène", "jacques", "sylvie"}
        For Each nom As String In amis
            Console.Out.WriteLine(nom)
        Next
    End Sub
End Module

将显示:

paul
hélène
jacques
sylvie

2.4.4.2. 重复次数未知

对于这种情况,VB.NET中存在许多结构。


Do { While | Until } condition
   [ statements ]
Loop

循环将持续执行,直到条件为真(while)或直到条件为真(until)。该循环可能永远不会被执行。


Do
   [ statements ]
Loop { While | Until } condition

循环将持续执行,直到条件为真(while)或直到条件为真(until)。循环至少会执行一次。


While condition
   [ statements ]
End While

只要条件成立,循环就继续。该循环可能永远不会被执行。以下循环均计算前10个整数的和。


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

Module boucles1
    Sub main()
        Dim i, somme As Integer
        i = 0 : somme = 0
        Do While i < 11
            somme += i
            i += 1
        Loop
        Console.Out.WriteLine("somme=" + somme.ToString)
        i = 0 : somme = 0
        Do Until i = 11
            somme += i
            i += 1
        Loop
        Console.Out.WriteLine("somme=" + somme.ToString)
        i = 0 : somme = 0
        Do
            somme += i
            i += 1
        Loop Until i = 11
        Console.Out.WriteLine("somme=" + somme.ToString)
        i = 0 : somme = 0
        Do
            somme += i
            i += 1
        Loop While i < 11
        Console.Out.WriteLine("somme=" + somme.ToString)
    End Sub
End Module
somme=55
somme=55
somme=55
somme=55

2.4.4.3. 循环管理说明

exit do
退出 do ... loop 循环
exit for
退出 for 循环

2.5. 程序结构 VB.NET

一个不使用用户定义类,且除主函数 Main 以外不使用其他函数的程序 VB.NET,其结构可能如下:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports espace1
Imports ....

Module nomDuModule
    Sub main()
....
    End Sub
End Module
  • 指令 [Option Explicit on] 强制要求声明变量。在 VB.NET 中,变量声明并非强制要求。未声明的变量将被视为 Object 类型。
  • 指令 [Option Strict on] 禁止任何可能导致数据丢失的数据类型转换,以及数值类型与字符串之间的转换。此时必须显式使用转换函数。
  • 程序会导入其所需的所有命名空间。我们尚未引入这一概念。在之前的程序中,我们经常遇到如下语句:
        Console.Out.WriteLine(unechaine)

实际上,我们本应这样写:

        System.Console.Out.WriteLine(unechaine)

其中 System 是包含类 [Console] 的命名空间。 通过 Imports 语句导入命名空间 [System] 后,当 VB.NET 遇到未知类时,它会系统地遍历该命名空间。它将遍历所有已声明的命名空间,直到找到目标类为止。因此,我们应写为:


' 命名空间
Imports System
....

        Console.Out.WriteLine(unechaine)

程序示例如下:


' 选项
Option Explicit On 
Option Strict On

'命名空间
Imports System

' 主模块
Module main1
    Sub main()
        Console.Out.WriteLine("main1")
    End Sub
End Module

该程序也可以如下编写:


' 选项
Option Explicit On 
Option Strict On

'命名空间
Imports System

' 测试类
Public Class main2
    Public Shared Sub main()
        Console.Out.WriteLine("main2")
    End Sub
End Class

这里我们使用了将在下一章介绍的类概念。当此类包含一个名为 main 的静态(shared)过程时,该过程将被执行。 我们在此引入这种写法,是因为VB.NET的姊妹语言C#只认识类概念,即c.a.d,且所有执行的代码都必须属于某个类。 类这一概念属于面向对象编程范畴。将其强加于所有程序的设计中略显笨拙。这在前文程序的第2版中可见一斑,其中我们在本无需使用类和静态方法的地方引入了这些概念。因此,今后我们仅在必要时才引入类这一概念。 在其他情况下,我们将像上文第1版那样使用模块的概念。

2.6. 编译并运行程序 VB.NET

编译程序 VB.NET 仅需 SDK.NET。请看以下程序:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

Module boucles1
    Sub main()
        Dim i, somme As Integer
        i = 0 : somme = 0
        Do While i < 11
            somme += i
            i += 1
        Loop
        Console.Out.WriteLine("somme=" + somme.ToString)
        i = 0 : somme = 0
        Do Until i = 11
            somme += i
            i += 1
        Loop
        Console.Out.WriteLine("somme=" + somme.ToString)
        i = 0 : somme = 0
        Do
            somme += i
            i += 1
        Loop Until i = 11
        Console.Out.WriteLine("somme=" + somme.ToString)
        i = 0 : somme = 0
        Do
            somme += i
            i += 1
        Loop While i < 11
        Console.Out.WriteLine("somme=" + somme.ToString)
    End Sub
End Module

假设该程序位于名为 [boucles1.vb] 的文件中。要编译它,请按以下步骤操作:

dos>dir boucles1.vb
11/03/2004  15:55                  583 boucles1.vb

dos>vbc boucles1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
pour Microsoft (R) .NET Framework version 1.1.4322.573
Copyright (C) Microsoft Corporation 1987-2002. Tous droits réservés.

dos>dir boucles1.*
11/03/2004  16:04                  601 boucles1.vb
11/03/2004  16:04                3 584 boucles1.exe

程序 vbc.exe 是编译器 VB.NET。在此,它位于 PATH 的 DOS 中:


dos>path
PATH=E:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE;E:\Program Files\Microsoft Visual Studio .NET 2003\VC7\BIN;E:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools;E:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\bin\prerelease;E:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\bin;E:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\bin;E:\WINNT\Microsoft.NET\Framework\v1.1.4322;e:\winnt\system32;e:\winnt;

dos>dir E:\WINNT\Microsoft.NET\Framework\v1.1.4322\vbc.exe
21/02/2003  10:20              737 280 vbc.exe

编译器 [vbc] 生成一个可由虚拟机执行的 .exe 文件 .NET:

dos>boucles1
somme=55
somme=55
somme=55
somme=55

2.7. 示例 IMPOTS

我们打算编写一个程序来计算纳税人的税款。我们假设一种简化情况,即纳税人只有工资需要申报:

  • 计算该雇员的税额份额 nbParts=nbEnfants/2 +1(若未婚), 已婚者为 nbEnfants/2+2,其中 nbEnfants 代表其子女数。
  • 若其子女数≥3,则额外增加半份
  • 计算其应税收入 R=0.72*S,其中 S 为其年薪
  • 计算其家庭系数 QF=R/nbParts
  • 计算其应纳税额 I。请看下表:
12620.0
0
0
13190
0.05
631
15640
0.1
1290.5
24740
0.15
2072.5
31810
0.2
3309.5
39970
0.25
4900
48360
0.3
6898.5
55790
0.35
9316.5
92970
0.4
12106
127860
0.45
16754.5
151250
0.50
23147.5
172040
0.55
30710
195000
0.60
39312
0
0.65
49062

每行有 3 个字段。要计算税款 I,需查找满足 QF<=字段1 的第一行。例如,若 QF=23000,则会找到该行

    24740        0.15        2072.5

此时税额 I 等于 0.15*R - 2072.5*nbParts。 如果 QF 使得关系 QF<=field1 从未成立,则使用最后一行中的系数。此处:

    0                0.65        49062

由此得出税额 I=0.65*R - 49062*nbParts。相应的程序 VB.NET 如下:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

Module impots
    ' ------------ 主
    Sub Main()

        ' 计算税款所需的数据表
        Dim Limites() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
        Dim CoeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}

        ' 获取婚姻状况
        Dim OK As Boolean = False
        Dim reponse As String = Nothing
        While Not OK
            Console.Out.Write("Etes-vous marié(e) (O/N) ? ")
            reponse = Console.In.ReadLine().Trim().ToLower()
            If reponse <> "o" And reponse <> "n" Then
                Console.Error.WriteLine("Réponse incorrecte. Recommencez")
            Else
                OK = True
            End If
        End While
        Dim Marie As Boolean = reponse = "o"

        ' 子女人数
        OK = False
        Dim NbEnfants As Integer = 0
        While Not OK
            Console.Out.Write("Nombre d'enfants : ")
            reponse = Console.In.ReadLine()
            Try
                NbEnfants = Integer.Parse(reponse)
                If NbEnfants >= 0 Then
                    OK = True
                Else
                    Console.Error.WriteLine("Réponse incorrecte. Recommencez")
                End If
            Catch
                Console.Error.WriteLine("Réponse incorrecte. Recommencez")
            End Try
        End While
        ' 工资
        OK = False
        Dim Salaire As Integer = 0
        While Not OK
            Console.Out.Write("Salaire annuel : ")
            reponse = Console.In.ReadLine()
            Try
                Salaire = Integer.Parse(reponse)
                If Salaire >= 0 Then
                    OK = True
                Else
                    Console.Error.WriteLine("Réponse incorrecte. Recommencez")
                End If
            Catch
                Console.Error.WriteLine("Réponse incorrecte. Recommencez")
            End Try
        End While
        ' 计算份额数
        Dim NbParts As Decimal
        If Marie Then
            NbParts = CDec(NbEnfants) / 2 + 2
        Else
            NbParts = CDec(NbEnfants) / 2 + 1
        End If
        If NbEnfants >= 3 Then
            NbParts += 0.5D
        End If
        ' 应税收入
        Dim Revenu As Decimal
        Revenu = 0.72D * Salaire

        ' 家庭分摊系数
        Dim QF As Decimal
        QF = Revenu / NbParts

        ' 查找与 QF 对应的税率档位
        Dim i As Integer
        Dim NbTranches As Integer = Limites.Length
        Limites((NbTranches - 1)) = QF
        i = 0
        While QF > Limites(i)
            i += 1
        End While
        ' 税额
        Dim impots As Integer = CInt(i * 0.05D * Revenu - CoeffN(i) * NbParts)

        ' 显示结果
        Console.Out.WriteLine(("Impôt à payer : " & impots))
    End Sub
End Module

该程序在 DOS 窗口中通过以下命令编译:

dos>vbc impots1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>dir impots1.exe
24/02/2004  15:42                5 632 impots1.exe

编译后生成可执行文件 impots.exe。 需要注意的是,impots.exe 无法被处理器直接执行。它实际上包含中间代码,该代码仅能在 .NET 平台上执行。所得结果如下:

dos>impots1
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : 3
Salaire annuel : 200000
Impôt à payer : 16400
dos>impots1
Etes-vous marié(e) (O/N) ? n
Nombre d'enfants : 2
Salaire annuel : 200000
Impôt à payer : 33388
dos>impots1
Etes-vous marié(e) (O/N) ? w
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? q
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : q
Réponse incorrecte. Recommencez
Nombre d'enfants : 2
Salaire annuel : q
Réponse incorrecte. Recommencez
Salaire annuel : 1
Impôt à payer : 0

2.8. 主程序的参数

主程序 Main 可以接受一个字符串数组作为参数:


    Sub main(ByVal args() As String)

参数 args 是一个字符串数组,用于接收调用程序时通过命令行传递的参数。

  • args.Length 是数组的元素个数args
  • args(i) 是数组的第 i 个元素

如果使用以下命令启动程序 P: P arg0 arg1 … argn,且程序 P 中的过程 Main 声明如下:


    Sub main(ByVal args() As String)

则 arg(0)="arg0", arg(1)="arg1" … 以下是一个示例:


' 说明
Option Strict On
Option Explicit On 

' 命名空间
Imports System

Module arg
    Sub main(ByVal args() As String)
        ' 参数个数
        console.out.writeline("Il y a " & args.length & " arguments")
        Dim i As Integer
        For i = 0 To args.Length - 1
            Console.Out.WriteLine("argument n° " & i & "=" & args(i))
        Next
    End Sub
End Module

执行结果如下:

dos>arg1 a b c
Il y a 3 arguments
argument n° 0=a
argument n° 1=b
argument n° 2=c

2.9. 枚举

枚举是一种数据类型,其取值范围是一组整数常量。假设有一个程序需要处理考试成绩。共有五种成绩:及格、AssezBien、良好、TrèsBien、优秀。因此,我们可以为这五个常量定义一个枚举:


    Enum mention
        Passable
        AssezBien
        Bien
        TrésBien
        Excellent
    End Enum

在内部,这五个常量由连续的整数编码,第一个常量从 0 开始,第二个从 1 开始,依此类推。可以声明一个变量来取枚举中的这些值:


        ' 从枚举中获取值的变量
        Dim maMention As mention = mention.Passable

可以将变量与枚举中的各种可能值进行比较:


        ' 使用枚举值进行测试
        If (maMention = mention.Passable) Then
            Console.Out.WriteLine("Peut mieux faire")
        End If

可以获取枚举的所有值:


        For Each m In mention.GetValues(maMention.GetType)
            Console.Out.WriteLine(m)
        Next

正如简单类型 Integer 等同于结构 Int32 一样,简单类型 Enum 也等同于结构 Enum。 该类有一个静态方法 GetValues,用于获取作为参数传递的枚举类型的所有值。该参数必须是 Type 类型的对象,Type 是一个关于数据类型的信息类。 变量 v 的类型可通过 v.GetType() 获取。 因此,此处 maMention.GetType() 返回枚举 mentions 的对象 Type,而 Enum.GetValues(maMention.GetType())则返回枚举 mentions 的值列表。

如下面的程序所示:


' 指令
Option Strict On
Option Explicit On 

' 命名空间
Imports System

Public Module enum2

    ' 一个枚举
    Enum mention
        Passable
        AssezBien
        Bien
        TrèsBien
        Excellent
    End Enum

    ' 测试页面
    Sub Main()

        ' 一个从枚举中获取值的变量
        Dim maMention As mention = mention.Passable

        ' 显示变量值
        Console.Out.WriteLine("mention=" & maMention)

        ' 使用枚举值的测试
        If (maMention = mention.Passable) Then
            Console.Out.WriteLine("Peut mieux faire")
        End If

        ' 字面量列表
        For Each m As mention In [Enum].GetValues(maMention.GetType)
            Console.Out.WriteLine(m.ToString)
        Next

        ' 完整标记列表
        For Each m As Integer In [Enum].GetValues(maMention.GetType)
            Console.Out.WriteLine(m)
        Next
    End Sub
End Module

运行结果如下:


dos>enum2
mention=0
Peut mieux faire
Passable
AssezBien
Bien
TrèsBien
Excellent
0
1
2
3
4

2.10. 异常处理

许多 VB.NET 函数可能会引发异常,即错误。当某个函数可能引发异常时,程序员应进行异常处理,以使程序更具容错性:必须始终避免应用程序发生“无预兆的崩溃”。

异常处理遵循以下流程:

try
    appel de la fonction susceptible de générer l'exception
catch  e as Exception e)
    traiter l'exception e
end try
instruction suivante

如果该函数未抛出异常,则跳转至下一条指令;否则,进入子句 catch 的主体,然后跳转至下一条指令。请注意以下几点:

  • e Exception 类型的派生对象。通过使用 IOException、SystemException 等类型可以更精确地指定:存在多种类型的异常。 编写 catch e as Exception 表示希望处理所有类型的异常。如果 try 子句的代码可能引发多种类型的异常,则可以通过使用多个 catch 子句来更精确地处理异常:
try
    appel de la fonction susceptible de générer l'exception
catch e as IOException 
    traiter l'exception e
catch  e as SystemException
    traiter l'exception e
end try
instruction suivante
  • 可以在 try/catch 子句中添加一个 finally 子句:
try
    appel de la fonction susceptible de générer l'exception
catch  e as Exception
    traiter l'exception e
finally
    code exécuté après try ou catch
end try
instruction suivante

无论是否发生异常,子句 finally 的代码都会被执行。

  • 在子句 catch 中,可能不希望使用可用的对象 Exception。因此,与其编写 catch e as Exception,不如直接编写 catch
  • Exception 具有一个名为 Message 的属性,该属性包含详细描述所发生错误的消息。因此,若要显示该消息,应编写如下代码:
catch e as Exception
    Console.Error.WriteLine("L'erreur suivante s'est produite : "+e.Message);
    ...
end try
  • Exception 包含一个方法 ToString,该方法返回一个字符串,其中包含异常类型以及属性 Message 的值。因此,我们可以这样编写:
catch  ex as Exception
    Console.Error.WriteLine("L'erreur suivante s'est produite : "+ex.ToString)
    ...
end try

以下示例展示了因使用不存在的数组元素而引发的异常:


' 选项
Option Explicit On 
Option Strict On

' 命名空间
Imports System

Module tab1
    Sub Main()
        ' 数组的声明与初始化
        Dim tab() As Integer = {0, 1, 2, 3}
        Dim i As Integer

        ' 使用 for 循环显示数组
        For i = 0 To tab.Length - 1
            Console.Out.WriteLine(("tab[" & i & "]=" & tab(i)))
        Next i

        ' 使用 for each 循环显示数组
        Dim élmt As Integer
        For Each élmt In tab
            Console.Out.WriteLine(élmt)
        Next élmt

        ' 抛出异常
        Try
            tab(100) = 6
        Catch e As Exception
            Console.Error.WriteLine(("L'erreur suivante s'est produite : " & e.Message))
        End Try
    End Sub
End Module

程序运行结果如下:

dos>exception1
tab[0]=0
tab[1]=1
tab[2]=2
tab[3]=3
0
1
2
3
L'erreur suivante s'est produite : L'index se trouve en dehors des limites du tableau.

以下是另一个示例,演示了当字符串不代表数字时,将字符串赋值给数字所引发的异常的处理方法:


' 选项
Option Strict On
Option Explicit On 

'导入
Imports System

Public Module console1
  Public Sub Main()
    ' 请求姓名
    System.Console.Write("Nom : ")

        ' 读取回答
    Dim nom As String = System.Console.ReadLine()

        ' 询问年龄
    Dim age As Integer
    Dim ageOK As Boolean = False
    Do While Not ageOK
      ' 提问
      Console.Out.Write("âge : ")
      ' 读取并验证回答
      Try
        age = Int32.Parse(System.Console.ReadLine())
        If age < 0 Then Throw New Exception
        ageOK = True
      Catch
        Console.Error.WriteLine("Age incorrect, recommencez...")
      End Try
    Loop

        ' 最终显示
    Console.Out.WriteLine("Vous vous appelez [" & nom & "] et vous avez [" & age & "] ans")
  End Sub
End Module

部分运行结果:

dos>console1
Nom : dupont
âge : 23
Vous vous appelez dupont et vous avez 23 ans
dos>console1
Nom : dupont
âge : xx
Age incorrect, recommencez...
âge : 12
Vous vous appelez dupont et vous avez 12 ans

2.11. 向函数传递参数

这里我们关注函数参数的传递方式。考虑以下函数:


    Sub changeInt(ByVal a As Integer)
        a = 30
        Console.Out.WriteLine(("Paramètre formel a=" & a))
    End Sub    

在函数定义中,a被称为形式参数。 它仅用于定义函数 changeInt。它同样可以命名为 b。现在让我们来看一下该函数的使用示例:


    Sub Main()
        Dim age As Integer = 20
        changeInt(age)
        Console.Out.WriteLine(("Paramètre effectif age=" & age))
    End Sub

在此 changeInt(age) 语句中,age 是将值传递给形式参数 a 的实际参数。 我们关注的是形式参数如何获取与其对应的有效参数的值。

2.11.1. 按值传递

以下示例表明,函数/过程的参数默认采用值传递:即实际参数的值会被复制到对应的形式参数中。二者是两个独立的实体。如果函数修改了形式参数,实际参数则完全不受影响。


' 选项
Option Explicit On 
Option Strict On

' 按值向函数传递参数
Imports System

Module param1
    Sub Main()
        Dim age As Integer = 20
        changeInt(age)
        Console.Out.WriteLine(("Paramètre effectif age=" & age))
    End Sub

    Sub changeInt(ByVal a As Integer)
        a = 30
        Console.Out.WriteLine(("Paramètre formel a=" & a))
    End Sub
End Module

所得结果如下:

Paramètre formel a=30
Paramètre effectif age=20

实际参数的值 20 被复制到了形式参数中 a。随后该形式参数被修改,而实际参数保持不变。这种传递方式适用于函数的输入参数。

2.11.2. 按引用传递

在引用传递中,实际参数和形式参数是同一个实体。如果函数修改了形式参数,实际参数也会随之改变。在 VB.NET 中,形式参数前必须加上关键字 ByRef。以下是一个示例:


' 选项
Option Explicit On 
Option Strict On

' 向函数传递按值传递的参数
Imports System

Module param2
    Sub Main()
        Dim age As Integer = 20
        changeInt(age)
        Console.Out.WriteLine(("Paramètre effectif age=" & age))
    End Sub

    Sub changeInt(ByRef a As Integer)
        a = 30
        Console.Out.WriteLine(("Paramètre formel a=" & a))
    End Sub
End Module

以及执行结果:

Paramètre formel a=30
Paramètre effectif age=30

实际参数已跟随形式参数的修改。这种传递方式适用于函数的输出参数。