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 类型
大小
值范围
Boolean
System.Boolean
2 字节
True 或 False。
字节
System.Byte
1 字节
0 到 255(无符号)。
Char
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)。
Double
System.Double
8字节
-1.79769313486231E+308 至
-4.94065645841247E-324(负值);4.94065645841247E-324 至 1.79769313486231E+308(正值)。
Integer
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。
Object
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。
String
System.String(类)
 
0 到大约 20 亿个 Unicode 字符。

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


Module types
    Sub Main()
        ' whole numbers
        Dim var1 As Integer = 100
        Dim var2 As Long = 10000000000L
        Dim var3 As Byte = 100
        Dim var4 As Short = 4
        ' real numbers
        Dim var5 As Decimal = 4.56789D
        Dim var6 As Double = 3.4
        Dim var7 As Single = -0.000103F
        ' date
        Dim var8 As Date = New Date(2003, 1, 1, 12, 8, 4)
        ' boolean
        Dim var9 As Boolean = True
        ' character
        Dim var10 As Char = "A"c
        ' character string
        Dim var11 As String = "abcde"
        ' object
        Dim var12 As Object = New Object
        ' displays
        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. 字面量数据表示法

整数
145、-7、&FF(十六进制)
长整型
100000L
双精度
134.789, -45E-18 (-45 × 10⁻¹⁸)
134.789F, -45E-18F (-45 × 10⁻¹⁸)
十进制
100000D
字符
"A"c
字符串
"today"
布尔值
true, false
日期
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 VAT_rate as single=0.186F]
  2. 如果需要更改“常量”,修改程序会更方便。因此,在前面的例子中,如果增值税税率变为 33%,唯一需要修改的就是更改定义其值的语句:[const vatrate as single=0.336F]。如果程序中显式使用了 0.186,则需要修改大量语句。

2.2.3.3. 变量声明

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

dim variable1,variable2,...,variablen as type_identifier

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

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

数字 -> 字符串
number.ToString 或 "" & number 或 CType(number, String)
对象 -> 字符串
object.ToString
字符串 -> 整数
Integer.Parse(字符串) 或 Int32.Parse
字符串 -> 长整型
Long.Parse(string) 或 Int64.Parse
字符串 -> 双精度
Double.Parse(string)
字符串 -> 单精度浮点数
Single.Parse(string)

如果字符串不表示一个有效的数字,将其转换为数字可能会失败。 这会导致一个致命错误,在 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 子句的主体,然后继续执行下一条语句。我们稍后将回到异常处理这一主题。以下是一个演示数字与字符串之间转换主要技巧的程序。在此示例中,函数 display 将其参数的值打印到屏幕上。因此,display(S) 会将 S 的值打印到屏幕上。


' guidelines
Option Strict On
 
' imported namespaces
Imports System
 
' the test module
Module Module1
 
    Sub Main()
        ' main proceedings
        ' local data
        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
 
        ' number --> string
        affiche(CType(i, String))
        affiche(CType(l, String))
        affiche(CType(f, String))
        affiche(CType(d, String))
 
        'boolean --> string
        Const b As Boolean = False
        affiche(b.ToString)
 
        ' string --> int
        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
 
        ' chain --> long
        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
 
        ' chain --> double
        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
 
        ' chain --> single
        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
 
    ' poster
    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 Array(n) As Type Dim Array() As Type = New Type(n) {}

其中 n 是数组最后一个元素的索引。语法 Array(i) 表示索引为 i 的数据,其中 i 属于 [0,n] 范围。任何对 Array(i) 的引用,如果 i 不属于 [0,n] 范围,都会引发异常。数组可以在声明时同时初始化。在这种情况下,无需指定最后一个元素的索引。


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

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


Module tab0
    Sub Main()
        ' a first picture
        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
 
        ' a second panel
        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 Array(n, m) As Type Dim Array(,) As Type = New Type(n, m) {}

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


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

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


Module Module2
    Sub Main()
        ' a first picture
        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
 
        ' a second panel
        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 Array(n)() As Type Dim Array()() As Type = New Type(n)()

上述声明创建了一个包含 n+1 行元素的数组。每个元素 Array(i) 都是一个一维数组的引用。在上述声明过程中,这些数组并未被创建。下面的示例演示了如何创建数组的数组:


        ' a table of tables
        Dim noms()() As String = New String(3)() {}
        ' initialization
        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

在此,names(i) 是一个包含 i+1 个元素的数组。由于 names(i) 是一个数组,因此 names(i).Length 表示它包含的元素个数。以下是一个结合了我们刚刚讨论的三种数组类型的示例:


' guidelines
Option Strict On
Option Explicit On 
 
' imports
Imports System
 
' test class
Module test
    Sub main()
        ' an initialized 1-dimensional array
        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
 
        ' an initialized 2-dimensional array
        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
 
        ' an array°of arrays
        Dim noms()() As String = New String(3)() {}
 
        ' initialization
        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
 
        ' display
        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)

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

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

  • Console.WriteLine()Console.Out.WriteLine() 效果相同,它将数据写入 Out 流,该流通常与屏幕相关联。
  • Console.Error.WriteLine() 写入 Error 流,该流通常也与屏幕相关联。

默认情况下,OutError 流与屏幕相关联。不过,正如我们稍后将看到的,它们可以在运行时重定向到文本文件。

2.3.2. 读取键盘输入的数据

来自键盘的数据流由类型为 StreamReaderConsole.In 对象表示。此类对象允许您使用 ReadLine 方法读取一行文本:


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

键盘输入的行被存储在变量 line* 中,随后程序即可使用该数据。与 OutError 流一样,In* 流也可以重定向到文件。

2.3.3. 输入/输出示例

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


' options
Option Explicit On 
Option Strict On
 
' namespaces
Imports System
 
' module
Module io1
    Sub Main()
        ' write to Out feed
        Dim obj As New Object
        Console.Out.WriteLine(("" & obj.ToString))
 
        ' write to the Error stream
        Dim i As Integer = 10
        Console.Error.WriteLine(("i=" & i))
 
        ' reading a line entered on the keyboard
        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 程序本身。设备 0、1 和 2 的性质由操作系统决定。请看以下程序:


' options
Option Explicit On 
Option Strict On
 
' namespaces
Imports System
 
' redirections
Module console2
    Sub Main()
        ' lecture flux In
        Dim data As String = Console.In.ReadLine()
        ' write Out feed
        Console.Out.WriteLine(("écriture dans flux Out : " + data))
        ' écriture flux 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. 运算符列表

操作
语言元素
算术
^, –, *, /, \, Mod, +, =
赋值
=, ^=, *=, /=, \=, +=, -=, &=
比较
=, <>, <, >, <=, >=, Like, Is
字符串连接
&, +
逻辑/位运算
非、与、或、异或、逻辑与、逻辑或
其他运算
AddressOf、GetType

2.3.5.2. 算术表达式

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

算术
^, –, *, /, \, Mod, +, =

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


' arithmetic operators
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]
重载 Public Shared Function Log( ByVal d As Double ) As Double
自然对数

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

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

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

Math 类的完整定义如下:

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

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

2.3.5.3. 算术表达式求值中的运算符

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

类别
运算符
一级
所有不含运算符的表达式:函数、括号
幂运算
^
一元取反
+, -
乘法
*, /
整数除法
\
取模
加法
+, -

2.3.5.4. 关系运算

运算符如下:

比较
=, <>, <, >, <=, >=, Like, Is

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

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


' namespaces
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. 布尔表达式

运算符如下:

逻辑/位运算
非、与、或、异或、全与、全或

非:逻辑与,或:逻辑或,非:否定,异或:异或。

op1 AndAlso op2:如果 op1 为假,则不评估 op2,结果为假。

op1 OrElse op2:如果 op1 为真,则不评估 op2,结果为真。

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

逻辑非
逻辑非
逻辑与
与、且
逻辑或
OR,OrElse
逻辑异或
异或

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

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 进行异或运算

考虑以下程序:


Module operationsbit
    Sub main()
        ' bit manipulation
        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。可与赋值运算结合使用的运算符列表如下:

运算符的组合
^=, *=, /=, \=, +=, -=, &=

2.3.5.8. 运算符的一般优先级

类别
运算符
主要
所有不含运算符的表达式
幂运算
^
一元取反
+, -
乘法
*, /
整数除法
\
取模
加法
+, -
连接
&
移位
<<, >>
关系
=, <>, <, >, <=, >=, Like, Is, TypeOf...Is
逻辑非
逻辑与
且,且且
逻辑或
OR,OrElse
逻辑异或
异或

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

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
双精度
-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+38 至 -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(表达式, 类型) 函数,如下面的程序所示:


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 环境下,该状态变量将通过系统变量 ERRORLEVEL 返回给 DOS,其值可在批处理文件中进行检查。在 Unix 环境下,变量 $? 可获取 exitCode 的值。

    Environment.Exit(0)

将以退出状态 0 终止程序。

2.4.2. 简单的决策结构

if condition then
    actions_alors
else
    actions_sinon
end if
  • 每个操作都应单独成行
  • else 子句可以省略。

您可以像以下示例所示那样嵌套决策结构:


' options
Option Explicit On 
Option Strict On
 
' namespaces
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
  • [表达式] 的类型必须是以下类型之一:
Boolean, Byte, Char, Date, Decimal, Double, Integer, Long, Object, Short, Single et String
  • [case else] 子句可以省略。
  • [值列表] 是表达式的可能取值。[条件列表] 表示一组条件 condition1、condition2、...、conditionx。如果 [表达式] 满足其中一个条件,则执行 [值列表] 子句后面的操作。条件可以采用以下形式:
  • val1 到 val2:如果 [表达式] 属于 [val1,val2] 范围,则为真
  • val1:若 [表达式] 等于 val1,则为真
  • is > val1:若 [表达式] > val1,则为真。关键字 [is] 可省略
  • 运算符 =、<、<=、>、>=、<> 也适用相同规则
  • 仅执行与第一个验证通过的条件相关联的操作。

请看以下程序:


' options
Option Explicit On 
Option Strict On
 
' namespaces
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] 取的每个值,都会执行相应的操作。请看以下程序:


' options
Option Explicit On 
Option Strict On

' namespaces
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 ]
  • 是一组对象。我们已经熟悉的对象集合就是数组
  • 数据类型是集合中对象的类型。对于数组而言,这即为数组元素的类型
  • 元素是循环内的局部变量,将依次取集合中的各个值。

因此,以下代码:


' options
Option Explicit On 
Option Strict On
 
' namespaces
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个整数的和。


' options
Option Explicit On 
Option Strict On
 
' namespaces
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 程序,其结构可能如下所示:


' options
Option Explicit On 
Option Strict On
 
' namespaces
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 遇到不认识的类时,它都会系统地搜索该命名空间。它会遍历所有已声明的命名空间进行搜索,直到找到目标类为止。因此,我们应写为:


' namespaces
Imports System
....
 
        Console.Out.WriteLine(unechaine)

一个示例程序可能如下所示:


' options
Option Explicit On 
Option Strict On
 
'namespaces
Imports System
 
' main module
Module main1
    Sub main()
        Console.Out.WriteLine("main1")
    End Sub
End Module

该程序也可以如下编写:


' options
Option Explicit On 
Option Strict On
 
'namespaces
Imports System
 
' test class
Public Class main2
    Public Shared Sub main()
        Console.Out.WriteLine("main2")
    End Sub
End Class

这里我们使用了类(class)的概念,该概念将在下一章中介绍。当此类包含一个名为 main 的静态(共享)过程时,该过程就会被执行。我们在此加入这段代码,是因为 VB.NET 的姊妹语言 C# 只支持类概念——也就是说,所有执行的代码都必须属于某个类。 类是面向对象编程的一部分。将其强加于每个程序的设计中多少有些不妥。我们在前一个程序的第 2 版中看到了这一点,在那里我们被迫在不需要的地方引入类和静态方法的概念。因此,从现在开始,我们只会在必要时引入类的概念。在其他情况下,我们将使用模块的概念,如上文的第 1 版所示。

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

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


' options
Option Explicit On 
Option Strict On
 
' namespaces
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

假设它位于一个名为 [loops1.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 编译器。在此,它位于 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. 税款计算示例

我们建议编写一个程序来计算纳税人的税款。我们考虑一种简化情况,即纳税人仅需申报工资收入:

  • 对于该员工,若其未婚,税级数 nbParts = nbEnfants / 2 + 1;若已婚,则 nbParts = nbEnfants / 2 + 2,其中 nbEnfants 表示子女数量。
  • 若其子女数至少为三名,则可额外获得半个税级
  • 我们计算其应税收入 R = 0.72 * S,其中 S 为其年薪
  • 我们计算其家庭系数 QF = R / nbParts
  • 我们计算其应纳税额 I。请看下表:
12620.0
0
0
13,190
0.05
631
15,640
0.1
1,290.5
24,740
0.15
2,072.5
31,810
0.2
3,309.5
39,970
0.25
4900
48,360
0.3
6,898.5
55,790
0.35
9,316.5
92,970
0.4
12,106
127,860
0.45
16,754.5
151,250
0.50
23,147.5
172,040
0.55
30,710
195,000
0.60
39,312
0
0.65
49062

每行有 3 个字段。要计算税款 I,请查找满足 QF <= 字段1 的第一行。例如,如果 QF = 23,000,则找到的行将是

    24740        0.15        2072.5

此时,Tax I 等于 0.15*R - 2072.5*nbParts。如果 QF 使得条件 QF<=field1 永远不成立,则使用最后一行中的系数。这里:

    0                0.65        49062

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


' options
Option Explicit On 
Option Strict On
 
' namespaces
Imports System
 
Module impots
    ' ------------ hand
    Sub Main()
 
        ' data tables required for tax calculation
        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}
 
        ' we recover marital status
        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"
 
        ' number of children
        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
        ' salary
        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
        ' calculating the number of shares
        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
        ' taxable income
        Dim Revenu As Decimal
        Revenu = 0.72D * Salaire
 
        ' family quotient
        Dim QF As Decimal
        QF = Revenu / NbParts
 
        ' search for tax bracket corresponding to 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
        ' tax
        Dim impots As Integer = CInt(i * 0.05D * Revenu - CoeffN(i) * NbParts)
 
        ' the result is displayed
        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" … 以下是一个示例:


' guidelines
Option Strict On
Option Explicit On 
 
' namespaces
Imports System
 
Module arg
    Sub main(ByVal args() As String)
        ' number of arguments
        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. 枚举

枚举是一种其值域为整数常量集合的数据类型。假设有一个需要处理考试成绩的程序。成绩共有五种:及格、尚可、良好、很好、优秀。那么,我们可以为这五个常量定义一个枚举:


    Enum mention
        Passable
        AssezBien
        Bien
        TrésBien
        Excellent
    End Enum

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


        ' a variable that takes its values from the enumeration mentioned
        Dim maMention As mention = mention.Passable

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


        ' test avec valeur de l'énumération
        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 枚举的值列表。

以下程序演示了这一点:


' guidelines
Option Strict On
Option Explicit On 
 
' namespaces
Imports System

Public Module enum2
 
    ' an enumeration
    Enum mention
        Passable
        AssezBien
        Bien
        TrèsBien
        Excellent
    End Enum
 
    ' test pg
    Sub Main()
 
        ' a variable that takes its values from the enumeration mentioned
        Dim maMention As mention = mention.Passable
 
        ' variable value display
        Console.Out.WriteLine("mention=" & maMention)
 
        ' test with enumeration value
        If (maMention = mention.Passable) Then
            Console.Out.WriteLine("Peut mieux faire")
        End If
 
        ' list of literal mentions
        For Each m As mention In [Enum].GetValues(maMention.GetType)
            Console.Out.WriteLine(m.ToString)
        Next
 
        ' list of full mentions
        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 写为 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

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


' options
Option Explicit On 
Option Strict On
 
' namespaces
Imports System
 
Module tab1
    Sub Main()
        ' declaring & initializing an array
        Dim tab() As Integer = {0, 1, 2, 3}
        Dim i As Integer
 
        ' table display with for
        For i = 0 To tab.Length - 1
            Console.Out.WriteLine(("tab[" & i & "]=" & tab(i)))
        Next i
 
        ' table display with a for each
        Dim élmt As Integer
        For Each élmt In tab
            Console.Out.WriteLine(élmt)
        Next élmt
 
        ' generating an exception
        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.

以下是另一个示例,其中我们处理了将字符串赋值给数字时(当该字符串不表示数字)引发的异常:


' options
Option Strict On
Option Explicit On 
 
'imports
Imports System
 
Public Module console1
  Public Sub Main()
    ' We ask for the name
    System.Console.Write("Nom : ")
 
        ' reading response
    Dim nom As String = System.Console.ReadLine()
 
        ' age requested
    Dim age As Integer
    Dim ageOK As Boolean = False
    Do While Not ageOK
      ' question
      Console.Out.Write("âge : ")
      ' reading-checking response
      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
 
        ' final display
    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. 按值传递

以下示例说明函数/过程的参数默认采用值传递:即实际参数的值会被复制到对应的形式参数中。它们是两个独立的实体。如果函数修改了形式参数,实际参数将保持不变。


' options
Option Explicit On 
Option Strict On
 
' passing parameters by value to a function
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 关键字。以下是一个示例:


' options
Option Explicit On 
Option Strict On
 
' passing parameters by value to a function
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

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