ZetCode--NET-教程-二-
ZetCode .NET 教程(二)
原文:ZetCode
基本概念
在 Visual Basic 教程的这一部分中,我们将介绍 Visual Basic 语言的基本编程概念。 我们介绍非常基本的程序。 我们将使用变量,常量和基本数据类型。 我们将在控制台上读写; 我们将提到变量插值。
下面是一个简单的 Visual Basic 程序。
Option Strict On
Module Example
Sub Main()
Console.WriteLine("This is Visual Basic")
End Sub
End Module
我们将逐行解释该程序。
Option Strict On
Option Strict
语句可以为On
或Off
。 默认值为Off
,以便与旧版 Visual Basic 程序向后兼容。 建议在所有新程序中使用此语句。 当该语句为On
时,编译器将检测各种不良的编程习惯。
Module Example
...
End Module
每个 Visual Basic 程序都应正确构造。 源代码放置在模块中。 在模块内,代码进一步分为过程和函数。 结构正确的模块更易于维护,并且不易出错。 在我们的例子中,我们创建一个名为Example
的模块。 为此,我们使用Module
关键字。 模块定义以End Module
语句结束。
Sub Main()
...
End Sub
使用Sub
关键字,创建一个过程。 Sub
是从子例程字派生的。 子例程和过程是同义词。 在 Visual Basic 中,首选词是过程。 过程是 Visual Basic 代码的一部分。 这是为了实现代码的模块化。
Console.WriteLine("This is Visual Basic")
在此代码行中,我们将"This is Visual Basic"
字符串字面值打印到控制台。 要将消息打印到控制台,我们使用Console
类的WriteLine()
方法。 它代表控制台应用的标准输入,输出和错误流。
$ ./simple.exe
This is Visual Basic
执行程序将得到以上输出。
我们也可以使用Console
类读取值。
Option Strict On
Module Example
Dim name As String
Sub Main()
Console.WriteLine("Enter your name: ")
name = Console.ReadLine()
Console.WriteLine("Hello {0}", name)
End Sub
End Module
第二个程序将从控制台读取一个值并打印出来。
Dim name As String
Dim
关键字用于声明变量。 该变量称为name
。 与常量(在程序生命周期内仅存储一个值)不同,变量可以存储各种不同的值。 As
关键字定义变量的数据类型。 我们的变量将保存字符串值。
name = Console.ReadLine()
我们从终端读取一行。 当我们按下Enter
键时,字符串将分配给name
变量。
Console.WriteLine("Hello {0}", name)
在此代码行中,我们执行变量插值。 变量插值正在用字符串字面值中的值替换变量。 变量插值的另一个名称是:变量替换和变量扩展。
$ ./readline.exe
Enter your name:
Jan
Hello Jan
这是第二个程序的输出。
命令行参数
Visual Basic 程序可以接收命令行参数。 有几种方法可以从命令行检索参数。
Option Strict On
Module Example
Dim cline As String
Sub Main()
cline = Command()
Console.WriteLine(cline)
End Sub
End Module
在上面的程序中,我们获取命令行参数并将其打印到终端。
cline = Command()
Command()
函数用于获取参数。
Console.WriteLine(cline)
我们将命令行参数打印到终端。
$ ./commandargs.exe 1 2 3
/home/vronskij/programming/basic/basics/commandargs.exe 1 2 3
我们执行程序,后跟三个数字。 程序将打印数字以及程序名称。 程序名称是第一个命令行参数。
在 Visual Basic 2008 Express Edition 中,选择项目属性。 在“调试”选项卡中,有一个文本区域用于指定命令行参数。
图:命令行参数
Option Strict On
Module Example
Dim size As Integer
Sub Main(ByVal cmdArgs() As String)
size = cmdArgs.Length
If size > 0 Then
For i As Integer = 0 To size - 1
Console.WriteLine(cmdArgs(i))
Next
End If
End Sub
End Module
命令行参数可以传递给Main
过程。
Sub Main(ByVal cmdArgs() As String)
此Main()
过程接收命令行参数的字符串数组。
size = cmdArgs.Length
我们确定数组的大小。
If size > 0 Then
For i As Byte = 0 To size - 1
Console.WriteLine(cmdArgs(i))
Next
End If
我们遍历数组并将所有参数打印到控制台。 请注意,在这种情况下,程序名称不包含在参数中。
$ ./cmdargs.exe 2 3 5
2
3
5
我们提供了三个数字作为命令行参数,并将它们打印到控制台上。
变量和常量
变量是存储数据的地方。 变量具有名称和数据类型。 数据类型确定可以为变量分配哪些值。 整数,字符串,布尔值等。在程序运行过程中,变量可以获得相同数据类型的各种值。 在对变量进行任何引用之前,始终将变量初始化为其类型的默认值。 变量用Dim
关键字声明。 与变量不同,常量保留其值。 一旦初始化,便无法修改。 用Const
关键字创建常量。
Option Strict On
Module Example
Sub Main()
Dim city As String = "New York"
Dim name As String = "Paul", age As Integer = 35, _
nationality As String = "American"
Console.WriteLine(city)
Console.WriteLine(name)
Console.WriteLine(age)
Console.WriteLine(nationality)
city = "London"
Console.WriteLine(city)
End Sub
End Module
在上面的示例中,我们使用四个变量。
Dim city As String = "New York"
我们声明一个String
类型的city
变量,并将其初始化为"New York"
值。
Dim name As String = "Paul", age As Integer = 35, _
nationality As String = "American"
我们可以使用一个Dim
关键字来声明和初始化更多变量; 它们之间用逗号分隔。
Console.WriteLine(city)
Console.WriteLine(name)
Console.WriteLine(age)
Console.WriteLine(nationality)
我们将变量的值打印到终端。
city = "London"
我们为城市变量分配一个新值。
正如我们上面已经说过的,常量不能更改其初始值。
Option Strict On
Module Example
Sub Main()
Const WIDTH As Integer = 100
Const HEIGHT As Integer = 150
Dim var As Integer = 40
var = 50
Rem WIDTH = 110
End Sub
End Module
在此示例中,我们声明两个常量和一个变量。
Const WIDTH As Integer = 100
Const HEIGHT As Integer = 150
我们使用Const
关键字通知编译器我们声明了一个常量。 按照惯例,用大写字母写常量。
Dim var As Integer = 40
var = 50
我们声明并初始化一个变量。 稍后,我们为变量分配一个新值。
Rem WIDTH = 110
使用常数是不可能的。 如果我们取消注释此行,则会收到编译错误。
变量插补
变量插值正在用字符串字面值中的值替换变量。 变量插值的另一个名称是:变量替换和变量扩展。
Option Strict On
Module Example
Dim age As Byte = 34
Dim name As String = "William"
Dim output As String
Sub Main()
output = String.Format("{0} is {1} years old.", _
name, age)
Console.WriteLine(output)
End Sub
End Module
在 Visual Basic 中,字符串是不可变的。 我们无法修改现有字符串。 变量插值发生在字符串创建期间。
Dim age As Byte = 34
Dim name As String = "William"
Dim output As String
在这里,我们声明三个变量。
output = String.Format("{0} is {1} years old.", _
name, age)
我们使用内置String
模块的Format()
方法。 {0}
和{1}
是求值变量的地方。 数字代表变量的位置。 {0}
计算得出的第一个变量,{1}
计算得出的第二个变量。
$ ./interpolation.exe
William is 34 years old.
输出。
本章介绍了 Visual Basic 语言的一些基础知识。
Visual Basic 数据类型
在 Visual Basic 教程的这一部分中,我们将讨论数据类型。
计算机程序可以处理数据。 用于各种数据类型的工具是现代计算机语言的基本组成部分。 根据维基百科的定义,data type
是一组值,以及对这些值的允许操作。
Visual Basic 中的两种基本数据类型是值类型和引用类型。 基本类型(字符串除外),枚举和结构是值类型。 类,字符串,标准模块,接口,数组和委托是引用类型。 每种类型都有一个默认值。 引用类型在堆上创建。 引用类型的生存期由 .NET 框架管理。 引用类型的默认值为空引用。 分配给引用类型的变量会创建引用的副本,而不是引用值的副本。 值类型在栈上创建。 生存期由变量的生存期决定。 分配给值类型的变量会创建要分配的值的副本。 值类型具有不同的默认值。 例如,布尔默认值为False
,十进制为 0,字符串为空字符串""
。
布尔值
我们的世界建立了双重性。 有天与地,水与火,阴与阳,男人与女人,爱与恨。 在 Visual Basic 中,Boolean
数据类型是具有以下两个值之一的原始数据类型:True
或False
。 这是基本的数据类型。 在计算机程序中非常常见。
快乐的父母正在等待孩子的出生。 他们为两种可能性都选择了名称。 如果要成为男孩,他们选择了约翰。 如果要成为女孩,他们会选择杰西卡。
Option Strict On
Module Example
Dim num As Byte
Dim male As Boolean
Sub Main()
Randomize()
num = CType(Math.Round(Rnd()), Byte)
If num = 0 Then
male = True
Else
male = False
End If
If male = True Then
Console.WriteLine("We will use name John")
Else
Console.WriteLine("We will use name Jessica")
End If
End Sub
End Module
该程序使用随机数生成器来模拟我们的情况。
Dim num As Byte
num
变量具有Byte
数据类型。 这个很重要。 字节数据类型的整数值可以为0..255
。
Randomize()
num = CType(Math.Round(Rnd()), Byte)
这两行随机取 0 或 1。Rnd()
函数返回小于 1 但大于或等于 0 的随机值。 我们使用Round()
方法将数字四舍五入。 大于 0.5 的随机值将四舍五入为 1。所有其他随机值将四舍五入为零。使用CType()
函数将Double
数据类型转换为Byte
。 因此,将num
变量分配为 0 或 1。Randomize()
函数根据系统计时器初始化带有种子的随机数生成器。
If num = 0 Then
male = True
Else
male = False
End If
根据num
变量,布尔值male
变量设置为True
或False
。
If male = True Then
Console.WriteLine("We will use name John")
Else
Console.WriteLine("We will use name Jessica")
End If
如果将男性变量设置为True
,则选择名称 John。 否则,我们选择名称 Jessica。 诸如If/Else
语句之类的控件结构可使用布尔值。
整数
整数是实数的子集。 它们写时没有小数或小数部分。 整数落入集合Z = {..., -2, -1, 0, 1, 2, ......}
中整数是无限的。
在计算机语言中,整数是原始数据类型。 实际上,计算机只能使用整数值的子集,因为计算机的容量有限。 整数用于计算离散实体。 我们可以有 3、4、6 个人,但不能有 3.33 个人。 我们可以有 3.33 公斤。
VB 别名 | .NET 类型 | 大小 | 范围 |
---|---|---|---|
SByte |
System.SByte |
1 字节 | -128 至 127 |
Byte |
System.Byte |
1 字节 | 0 至 255 |
Short |
System.Int16 |
2 字节 | -32,768 至 32,767 |
UShort |
System.UInt16 |
2 字节 | 0 至 65,535 |
Integer |
System.Int32 |
4 字节 | -2,147,483,648 至 2,147,483,647 |
UInteger |
System.UInt32 |
4 字节 | 0 至 4,294,967,295 |
Long |
System.Int64 |
8 字节 | -9,223,372,036,854,775,808 至 9,223,372,036,854,775,807 |
ULong |
System.UInt64 |
8 字节 | 0 至 18,446,744,073,709,551,615 |
可以根据我们的需要使用这些整数类型。 没有人(也许有些圣经人除外)的年龄可以超过 120、130 岁。 然后,我们可以在程序中将Byte
类型用于年龄变量。 这样可以节省一些内存。
Option Strict On
Module Example
Sub Main()
Dim a As Byte = 254
Console.WriteLine(a)
a += 1
Console.WriteLine(a)
a += 1
Console.WriteLine(a)
End Sub
End Module
在此示例中,我们尝试分配一个超出数据类型范围的值。 Mono 编译器给出System.OverflowException
。 Visual Basic 2008 Express 将不会编译此示例。
在 Visual Basic 中,可以使用三种不同的表示法指定整数:十进制,十六进制和八进制。 八进制值以&o
开头,十六进制以&h
开头。
Option Strict On
Module Example
Sub Main()
Dim num1 As Byte = 31
Dim num2 As Byte = &o31
Dim num3 As Byte = &h31
Console.WriteLine(num1)
Console.WriteLine(num2)
Console.WriteLine(num3)
End Sub
End Module
我们使用三个符号将 31 分配给三个变量。 然后将它们打印到控制台。
$ mono intnotations.exe
31
25
49
默认符号是十进制。 程序以十进制显示这三个数字。
如果我们使用整数,那么我们将处理离散实体。 我们将使用整数来计算苹果。
Option Strict On
Module Example
Sub Main()
Rem number of baskets
Dim baskets As Byte = 16
Rem number of apples in each basket
Dim apples_in_basket As Byte = 24
Rem total number of apples
Dim total As Short = baskets * apples_in_basket
Console.WriteLine("There are total of {0} apples", total)
End Sub
End Module
在我们的程序中,我们计算了苹果的总量。 我们使用乘法运算。
$ mono apples.exe
There are total of 384 apples
程序的输出。
浮点数
浮点数表示计算中的实数。 实数测量连续的数量,例如重量,高度或速度。 在 Visual Basic 中,我们有三种重要的浮点类型:Single
,Double
和Decimal
。
VB 别名 | .NET 类型 | 大小 | 精度 | 范围 |
---|---|---|---|---|
Single |
System.Single |
4 字节 | 7 位数 | 1.5 x 10^-45 至3.4 x 10^38 |
Double |
System.Double |
8 字节 | 15-16 位数 | 5.0 x 10^-324 至1.7 x 10^308 |
Decimal |
System.Decimal |
16 字节 | 28-29 位小数 | 1.0 x 10^-28 至7.9 x 10^28 |
上表给出了浮点类型的特征。
我们可以使用各种语法来创建浮点值。
Option Strict On
Module Example
Sub Main()
Dim n1 As Single = 1.234
Dim n2 As Single = 1.2e-3
Dim n3 As Single = 1 / 3
Console.WriteLine(n1)
Console.WriteLine(n2)
Console.WriteLine(n3)
End Sub
End Module
我们有三种创建浮点值的方法。 第一种是使用小数点的“正常”方式。 第二种使用科学计数法。 最后一个是数字运算的结果。
$ mono fnotations.exe
1.234
0.0012
0.3333333
这是上面程序的输出。
Option Strict On
Module Example
Sub Main()
Dim n1 As Single = 1 / 3
Dim n2 As Double = 1 / 3
If n1 = n2 Then
Console.WriteLine("Numbers are equal")
Else
Console.WriteLine("Numbers are not equal")
End If
End Sub
End Module
Single
和Double
值以不同的精度存储。 比较浮点值时应格外小心。
$ mono fequal.exe
Numbers are not equal
而且数字不相等。
假设一个短跑运动员跑了 1 个小时,跑了 9.87 秒。 他的公里/小时速度是多少?
Option Strict On
Module Example
Sub Main()
Dim distance As Single
Dim time As Single
Dim speed As Single
Rem 100m is 0.1 km
distance = 0.1
Rem 9.87s is 9.87/60*60 h
time = 9.87 / 3600
speed = distance / time
Console.WriteLine("The average speed of a sprinter is {0} km/h", speed)
End Sub
End Module
在此示例中,必须使用浮点值。
speed = distance / time
为了获得速度,我们将距离除以时间。
$ mono sprinter.exe
The average speed of a sprinter is 36.47416 km/h
这是短跑运动员程序的输出。
枚举
枚举类型(也称为枚举或枚举)是由一组命名值组成的数据类型。 可以将任何枚举器分配为已声明为具有枚举类型的变量作为值。 枚举使代码更具可读性。
Option Strict On
Module Example
Enum Days
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
End Enum
Sub Main()
Dim day As Days = Days.Monday
If day = Days.Monday
Console.WriteLine("It is Monday")
End If
Console.WriteLine(day)
End Sub
End Module
在我们的代码示例中,我们为工作日创建一个枚举。
Enum Days
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
End Enum
使用Enum
关键字创建枚举。 星期一,星期二...星期日实际上存储数字0..6
。
Dim day As Days = Days.Monday
我们有一个名为day
的变量,其类型为Days
。 它被初始化为星期一。
If day = Days.Monday
Console.WriteLine("It is Monday")
End If
与将日变量与某个数字进行比较相比,此代码更具可读性。
Console.WriteLine(day)
此行将 0 打印到控制台。 对于计算机,枚举是数字。
我们会进一步进行枚举。
Option Strict On
Module Example
Enum Seasons As Byte
Spring = 1
Summer = 2
Autumn = 3
Winter = 4
End Enum
Sub Main()
Dim s1 As Seasons = Seasons.Spring
Dim s2 As Seasons = Seasons.Autumn
Console.WriteLine(s1)
Console.WriteLine(s2)
End Sub
End Module
季节可以很容易地用作枚举。 我们可以为枚举指定基础类型,还可以为其提供确切的值。
Enum Seasons As Byte
Spring = 1
Summer = 2
Autumn = 3
Winter = 4
End Enum
使用As
关键字,我们指定枚举的基础类型。 我们还给每个成员一个特定的号码。
Console.WriteLine(s1)
Console.WriteLine(s2)
这两行将 1、3 打印到控制台。
字符串和字符
String
是代表计算机程序中文本数据的数据类型。 Visual Basic 中的字符串是 Unicode 字符序列。 Char
是单个 Unicode 字符。 字符串用单引号或双引号引起来。
由于字符串在每种编程语言中都非常重要,因此我们将为它们专门整整一章。 在这里,我们仅举一个小例子。
Option Strict On
Module Example
Sub Main()
Dim word As String = "ZetCode"
Dim c As Char = CType(word.SubString(0, 1), Char)
Console.WriteLine(c)
End Sub
End Module
程序将Z
字符打印到终端。
Dim word As String = "ZetCode"
在这里,我们创建一个字符串变量,并为其分配"ZetCode"
值。
Dim c As Char = CType(word.SubString(0, 1), Char)
SubString()
方法用于从字符串返回子字符串。 方法返回类型为String
。 我们要检索并分配一个字符。 这就是为什么我们需要使用CType
函数将String
转换为Char
的原因。 字符随后被打印到终端。
数组
数组是处理元素集合的复杂数据类型。 每个元素都可以通过索引访问。 数组的所有元素必须具有相同的数据类型。
我们将一整章专门介绍数组,这里仅显示一个小例子。
Option Strict On
Module Example
Sub Main()
Dim array(5) As Integer
array(0) = 3
array(1) = 2
array(2) = 1
array(3) = 5
array(4) = 6
For i As Integer = 0 To array.Length-1
Console.WriteLine(i)
Next
End Sub
End Module
在此示例中,我们声明一个数组,用数据填充它,然后将数组的内容打印到控制台。
Dim array(5) As Integer
我们声明一个整数数组,该数组最多可以存储 5 个整数。
array(0) = 3
array(1) = 2
array(2) = 1
array(3) = 5
array(4) = 6
在这里,我们为创建的数组分配值。
For i As Integer = 0 To array.Length-1
Console.WriteLine(i)
Next
我们遍历数组并将数据打印到控制台。 数组的Length
属性为我们提供了相关数组的长度。
日期
Date
是值类型,其中包含日期值,时间值或日期和时间值。
Option Strict On
Module Example
Sub Main()
Dim today As Date
today = Now()
System.Console.WriteLine(today)
System.Console.WriteLine(today.ToShortDateString())
System.Console.WriteLine(today.ToShortTimeString())
End Sub
End Module
我们以三种不同的格式显示今天的日期。 日期&时间,日期和时间。
Dim today As Date
我们声明一个Date
数据类型的变量。
today = Now()
返回当前日期和时间,使用计算机的系统日期和时间。
System.Console.WriteLine(today)
此行以完整格式打印日期。
System.Console.WriteLine(today.ToShortDateString())
System.Console.WriteLine(today.ToShortTimeString())
ToShortDateString()
返回短日期字符串格式,ToShortTimeString()
返回短时间字符串格式。
$ mono date.exe
8/17/2010 1:19:05 PM
8/17/2010
1:19 PM
示例的输出。
类型转换
我们经常一次处理多种数据类型。 将一种数据类型转换为另一种数据类型是编程中的常见工作。 类型转换或类型转换是指将一种数据类型的实体更改为另一种。 有两种类型的转换。 隐式和显式。 隐式类型转换,也称为强制转换,是编译器自动进行的类型转换。
Rem Option Strict On
Module Example
Sub Main()
Dim val As Byte
val = 0.54
Console.WriteLine(val)
Console.WriteLine(12 + 12.5)
Console.WriteLine("12" + 12)
Console.WriteLine("12" & 12)
End Sub
End Module
在此示例中,我们有一堆隐式转换。
Rem Option Strict On
当Option Strict
语句为On
时,某些隐式转换是不可能的。 这就是为什么我们注释这一行。
val = 0.54
浮点值分配给Byte
数据类型的变量。 它是整数数据类型。 取整为 1; 失去一些精度。 这是缩小隐式转换。
Console.WriteLine(12 + 12.5)
我们添加两个值。 一整数和一浮点值。 结果是浮点值。 这是扩展的隐式转换。
Console.WriteLine("12" + 12)
结果为 24。该字符串被隐式转换为数字。
Console.WriteLine("12" & 12)
结果为 1212。将整数转换为字符串,然后将两个字符串相加。
接下来,我们将显示 Visual Basic 中的一些显式转换。
函数 | 转换为 |
---|---|
CBool |
Boolean |
CByte |
Byte |
CChar |
Unicode character |
CDate |
Date |
CDb1 |
Double |
CDec |
Decimal |
CInt |
Integer |
CLng |
Long |
CObj |
Object |
CShort |
Short |
CSng |
Single |
CStr |
String |
我们有几个函数可以执行显式转换。
Option Strict On
Module Example
Sub Main()
Console.WriteLine(CByte(0.4))
Console.WriteLine(CByte(0.9))
Console.WriteLine(CShort(12 + 12.5))
Console.WriteLine(Val("1071 Fifth Avenue"))
End Sub
End Module
在程序中,我们执行三种类型的转换。
Console.WriteLine(CByte(0.4))
Console.WriteLine(CByte(0.9))
这两行执行缩小的显式转换。 第一个数字四舍五入为 0,第二个数字四舍五入为 1。
Console.WriteLine(Val("1071 Fifth Avenue"))
Val()
函数返回字符串中包含的数字。 在我们的情况下为 1071。
$ mono explicit.exe
0
1
24
1071
示例的输出。
在 Visual Basic 教程的这一部分中,我们介绍了数据类型及其转换。
Visual Basic 中的字符串
在 Visual Basic 教程的这一部分中,我们将更详细地处理字符串数据。
字符串是计算机语言中最重要的数据类型。 这就是为什么我们将一整章专门讨论在 Visual Basic 中使用字符串的原因。
第一个例子
字符串字面值是表示计算机程序文本内的字符串值的符号。 在 Visual Basic 中,字符串字面值用双引号引起来。 Visual Basic 中的字符串是 Unicode 字符序列。
Option Strict On
Module Example
Sub Main()
Dim str1 As String = "There are 10"
Dim str2 As String = " apples"
Console.WriteLine(str1 + str2)
Console.WriteLine("The length of the first string is " _
+ str1.Length.ToString() + " characters")
End Sub
End Module
在前面的示例中,我们创建了两个字符串变量。 然后我们将它们相加并计算第一个字符串的长度。
Dim str1 As String = "There are 10"
声明并初始化一个字符串变量。
Console.WriteLine(str1 + str2)
两个字符串连接在一起。 我们使用+
运算符添加两个字符串。
Console.WriteLine("The length of the first string is " _
+ str1.Length.ToString() + " characters")
Length
属性用于确定字符串的长度。
$ ./basics.exe
There are 10 apples
The length of the first string is 12 characters
运行示例可得出此结果。
使用引号
双引号用于在 Visual Basic 中创建字符串字面值。 如果我们想显示报价,例如直接讲话怎么办? 要打印双引号,必须在其前面加上另一个双引号。
Option Strict On
Module Example
Sub Main()
Console.WriteLine("There are many stars.")
Console.WriteLine("He said, ""Which one is your favourite?""")
End Sub
End Module
在控制台上打印双引号时,必须在其前面加上另一个双引号。
Console.WriteLine("He said, ""Which one is your favourite?""")
在这里,我们展示了如何将直接语音打印到控制台。 如果我们不使用两个双引号,则会误导编译器。 它会看到两个连续的字符串。
$ ./quotes.exe
There are many stars.
He said, "Which one is your favourite?"
输出。
多行字符串
可以在 Visual Basic 中创建多行字符串。
Option Strict On
Module Example
Sub Main()
Dim multiString As String = "I cheated myself" + vbNewLine + _
"like I knew I would" + vbNewLine + _
"I told ya, I was trouble" + vbNewLine + _
"you know that I'm no good"
Console.WriteLine(multiString)
End Sub
End Module
该示例创建一个跨越多行的字符串。 我们使用换行符,加号和vbNewLine
显示常量。
$ ./multiline.exe
I cheated myself
like I knew I would
I told ya, I was trouble
you know that I'm no good
此文本以四行显示。 所有文本都分配给一个字符串变量。
比较字符串
Option Strict On
Module Example
Sub Main()
Console.WriteLine("12" = "12") 'Returns True
Console.WriteLine("17" < "9") ' Returns True
Console.WriteLine("aa" > "ab") ' Returns False
End Sub
End Module
比较运算符在字符串上下文中的工作方式不同。
Console.WriteLine("17" < "9") 'Returns True
值 17 不小于 9。但是在两个字符串上应用<
时,我们不比较数字。 我们比较字符的排序顺序。 1 在 9 之前,因此具有“较低位置”,并且比较返回True
。
Console.WriteLine("aa" > "ab") ' Returns False
如果前两个字符相等,则继续对后两个字符进行操作。 a 字符位于 b 之前,并且比较操作返回False
。
有String.Compare()
方法,该方法比较两个指定的字符串并返回一个整数,该整数指示它们在排序顺序中的相对位置。 如果返回的值小于零,则第一个字符串小于第二个字符串。 如果返回零,则两个字符串相等。 最后,如果返回的值大于零,则第一个字符串大于第二个字符串。
Option Strict On
Module Example
Sub Main()
Dim str1 As String = "Visual Basic"
Dim str2 As String = "visual basic"
Console.WriteLine(String.Compare(str1, str2, True))
Console.WriteLine(String.Compare(str1, str2, False))
End Sub
End Module
第三个可选的ignoreCase
参数确定是否应履行该案件。
Console.WriteLine(String.Compare(str1, str2, True))
比较两个字符串并忽略大小写。 此行将 0 打印到控制台。
有一个Like
运算符,可用于简单的正则表达式匹配。
Option Strict On
Module Example
Dim words() As String = {"Seven", "even", "Maven", "Amen", "Leven"}
Sub Main()
For Each word As String In words
If word Like "?*even" Then
Console.WriteLine("{0} matches the pattern", word)
Else
Console.WriteLine("{0} does not match the pattern", word)
End If
Next
End Sub
End Module
我们有很多单词。 我们将针对正则表达式模式测试这些单词。 如果单词匹配或不匹配,我们将向控制台打印一条消息。
Dim words() As String = {"Seven", "even", "Maven", "Amen", "Leven"}
这是五个单词的数组。
For Each word As String In words
...
Next
我们使用For Each
循环遍历数组。 当前单词存储在单词变量中。
If word Like ".*even" Then
Console.WriteLine("{0} matches the pattern", word)
Else
Console.WriteLine("{0} does not match the pattern", word)
End If
"?*even"
是一个简单的正则表达式模式。 ?
匹配任何单个字符,*
零个或多个字符。 我们打印一条消息以通知单词是否与模式匹配。
字符串函数
Visual Basic 具有有用的内置函数,可用于处理字符串。
Option Strict On
Module Example
Sub Main()
Dim str As String = "Visual Basic"
Dim n As Integer = Len(str)
Dim l As String = Left(str, 6)
Dim r As String = Right(str, 5)
Dim repl As String = Replace(str, "Basic", "form")
Console.WriteLine("The string has {0} characters", n)
Console.WriteLine("The Left function returns {0}", l)
Console.WriteLine("The Right function returs {0}", r)
Console.WriteLine("The Replace function returns {0}", repl)
End Sub
End Module
我们在 Visual Basic 中引入了四个字符串函数。
Dim n As Integer = Len(str)
Len()
函数返回字符串中的字符数。
Dim l As String = Left(str, 6)
此Left()
函数的调用从字符串的左侧返回 6 个字符。 在我们的例子中,Visual
。
Dim r As String = Right(str, 5)
在这里,我们从右边得到 5 个字符。
Dim repl As String = Replace(str, "Basic", "form")
字符串在 Visual Basic 中是不可变的。 当我们使用Replace()
函数时,我们返回一个新的修改后的字符串,其中第一个字符串替换为第二个字符串。
$ ./strfunc.exe
The string has 12 characters
The Left function returns Visual
The Right function returs Basic
The Replace function returns Visual form
运行该示例将得出前面的结果。
Join()
和Split()
函数非常方便。
Option Strict On
Imports System
Module Example
Sub Main()
Dim items() As String = {"C#", "Visual Basic", "Java", "Perl"}
Dim langs As String = Join(items, ",")
Console.WriteLine(langs)
Dim ls() As String = Split(langs, ",")
For Each lang As String In ls
Console.WriteLine(lang)
Next
End Sub
End Module
在我们的程序中,我们将使用这两个函数来连接和分割字符串。
Dim langs As String = Join(items, ",")
数组中的所有单词都被加入。 我们从中构建一个字符串。 每两个字之间会有一个逗号。
Dim ls() As String = Split(langs, ",")
作为反向操作,我们分割了langs
字符串。 Split()
函数返回一个由字符分隔的单词数组。 在我们的情况下,它是一个逗号字符。
For Each lang As String In ls
Console.WriteLine(lang)
Next
我们遍历数组并打印其元素。
$ ./joinsplit.exe
C#,Visual Basic,Java,Perl
C#
Visual Basic
Java
Perl
示例的输出。
字符串方法
除了字符串函数,还有几种字符串方法。 其中一些提供相同的功能。 正如我们已经提到的,字符串不是原始数据类型。 它们是引用类型。 它们是对象,这些对象具有可以完成某些工作的方法。
Option Strict On
Imports System
Module Example
Sub Main()
Dim str As String = "Determination"
Console.WriteLine(str.Contains("e"))
Console.WriteLine(str.IndexOf("e"))
Console.WriteLine(str.LastIndexOf("i"))
Console.WriteLine(str.ToUpper)
Console.WriteLine(str.ToLower)
End Sub
End Module
在上面的示例中,我们介绍了五个字符串方法。
Console.WriteLine(str.Contains("e"))
如果字符串包含特定字符,则Contains()
方法返回True
。
Console.WriteLine(str.IndexOf("e"))
IndexOf
返回字符串中字母的第一个索引。
Console.WriteLine(str.LastIndexOf("i"))
LastIndexOf()
方法返回字符串中字母的最后一个索引。
Console.WriteLine(str.ToUpper)
Console.WriteLine(str.ToLower)
字符串的字母通过ToUpper
方法转换为大写,并通过ToLower
方法转换为小写。
$ ./strmethods.exe
True
1
10
DETERMINATION
determination
运行程序。
复制与克隆
我们将描述两种方法之间的区别。 复制并克隆。 Copy()
方法创建一个新的String
实例,该实例的值与指定的String
相同。 Clone()
方法返回对正在克隆的字符串的引用。 它不是堆上字符串的独立副本。 它是同一字符串上的另一个引用。
Option Strict On
Module Example
Sub Main()
Dim str As String = "Visual Basic"
Dim cloned As String = CType(str.Clone(), String)
Dim copied As String = String.Copy(str)
Console.WriteLine(str = cloned) ' Prints True
Console.WriteLine(str = copied) ' Prints True
Console.WriteLine(str Is cloned) ' Prints True
Console.WriteLine(str Is copied) ' Prints False
End Sub
End Module
我们的示例演示了两种方法之间的区别。
Dim cloned As String = CType(str.Clone(), String)
Dim copied As String = String.Copy(str)
字符串值被克隆并复制。
Console.WriteLine(str = cloned) ' Prints True
Console.WriteLine(str = copied) ' Prints True
所有三个字符串的内容都是相同的。
Console.WriteLine(str Is cloned) ' Prints True
Console.WriteLine(str Is copied) ' Prints False
Is
运算符比较两个引用对象。 因此,将复制的字符串与原始字符串进行比较将返回False
。 因为它们是两个不同的对象。
格式化字符串
在下面的示例中,我们将格式化字符串。 .NET Framework 具有称为复合格式的功能。 Format()
和WriteLine()
方法支持它。 方法采用对象列表和复合格式字符串作为输入。 格式字符串由固定字符串加上一些格式项组成。 这些格式项是与列表中的对象相对应的索引占位符。
格式项具有以下语法:
{index[,length][:formatString]}
索引组件是必需的。 它是一个从 0 开始的数字,表示对象列表中的一项。 多个项目可以引用对象列表的同一元素。 如果格式项未引用该对象,则将其忽略。 如果我们在对象列表的范围之外引用,则会抛出运行时异常。
长度部分是可选的。 它是参数的字符串表示形式中的最小字符数。 如果为正,则该参数为右对齐;否则为 0。 如果为负,则为左对齐。 如果指定,则必须用冒号分隔索引和长度。
formatString
是可选的。 它是一个格式化值的字符串,是一种特定的方式。 它可以用来格式化日期,时间,数字或枚举。
在这里,我们展示了如何使用格式项的长度分量。 我们将三列数字打印到终端。 左,中和右对齐。
Option Strict On
Imports System
Module Example
Dim oranges As Byte = 2
Dim apples As Byte = 4
Dim bananas As Byte = 3
Sub Main()
Dim str1 As String = "There are {0} oranges, {1} apples and " + _
"{2} bananas"
Dim str2 As String = "There are {1} oranges, {2} bananas and " + _
"{0} apples"
Console.WriteLine(str1, oranges, apples, bananas)
Console.WriteLine(str2, apples, oranges, bananas)
End Sub
End Module
我们向控制台打印一条简单的消息。 我们仅使用格式项的索引部分。
Dim str1 As String = "There are {0} oranges, {1} apples and " + _
"{2} bananas"
{0},{1}和{2}是格式项。 我们指定索引组件。 其他组件是可选的。
Console.WriteLine(str1, oranges, apples, bananas)
现在,我们将复合格式放在一起。 我们有字符串和对象列表(橙色,苹果,香蕉)。 {0}
格式项目是指橙色。 WriteLine()
方法将{0}
格式项替换为oranges
变量的内容。
Dim str2 As String = "There are {1} oranges, {2} bananas and " + _
"{0} apples"
引用对象的格式项的顺序很重要。
$ ./format1.exe
There are 2 oranges, 4 apples and 3 bananas
There are 2 oranges, 3 bananas and 4 apples
Option Strict On
Module Example
Sub Main()
Console.WriteLine("{0} {1, 12}", _
"Decimal", "Hexadecimal")
Console.WriteLine("{0:D} {1,8:X}", _
502, 546)
Console.WriteLine("{0:D} {1,8:X}", _
345, 765)
Console.WriteLine("{0:D} {1,8:X}", _
320, 654)
Console.WriteLine("{0:D} {1,8:X}", _
120, 834)
Console.WriteLine("{0:D} {1,8:X}", _
620, 454)
End Sub
End Module
我们以十进制和十六进制格式打印数字。 我们还使用长度分量对齐数字。
Console.WriteLine("{0:D} {1,8:X}", _
502, 546)
{0:D}
格式项指定,将采用提供的对象列表中的第一项并将其格式化为十进制格式。 {1,8:X}
格式项目取第二项。 将其格式化为十六进制格式(:X)
。 字符串长度为 8 个字符,8
。 因为数字只有三个字符,所以它会右对齐并用空字符串填充。
$ ./format2.exe
Decimal Hexadecimal
502 222
345 2FD
320 28E
120 342
620 1C6
运行示例。
最后两个示例将格式化数字和日期数据。
Option Strict On
Module Example
Sub Main()
Console.WriteLine(String.Format("Number: {0:N}", 126))
Console.WriteLine(String.Format("Scientific: {0:E}", 126))
Console.WriteLine(String.Format("Currency: {0:C}", 126))
Console.WriteLine(String.Format("Percent: {0:P}", 126))
Console.WriteLine(String.Format("Hexadecimal: {0:X}", 126))
End Sub
End Module
该示例演示了数字的标准格式说明符。 数字 126 以五种不同的格式打印。 正常,科学,货币,百分比和十六进制。
$ ./format3.exe
Number: 126.00
Scientific: 1.260000E+002
Currency: $126.00
Percent: 12,600.00 %
Hexadecimal: 7E
输出。
最后,我们将格式化日期和时间数据。
Option Strict On
Module Example
Sub Main()
Dim today As DateTime = DateTime.Now()
Console.WriteLine(String.Format("Short date: {0:d}", today))
Console.WriteLine(String.Format("Login date: {0:D}", today))
Console.WriteLine(String.Format("Short time: {0:t}", today))
Console.WriteLine(String.Format("Long time: {0:T}", today))
Console.WriteLine(String.Format("Month: {0:M}", today))
Console.WriteLine(String.Format("Year: {0:Y}", today))
End Sub
End Module
前面的示例演示了日期的标准格式说明符。
$ ./format4.exe
Short date: 8/18/2010
Login date: Wednesday, August 18, 2010
Short time: 11:29 PM
Long time: 11:29:40 PM
Month: August 18
Year: August, 2010
Output.
Visual Basic 教程的这一部分介绍了字符串。
运算符
在 Visual Basic 教程的这一部分中,我们将讨论运算符。
运算符是特殊符号,表示已执行某个过程。 编程语言的运算符来自数学。 程序员处理数据。 运算符用于处理数据。
我们有几种类型的运算符:
- 算术运算符
- 布尔运算符
- 关系运算符
- 按位运算符
一个运算符可以有一个或两个操作数。 操作数是运算符的输入(参数)之一。 仅使用一个操作数的那些运算符称为一元运算符。 那些使用两个操作数的对象称为二进制运算符。
Option Strict On
Module Example
Sub Main()
Console.WriteLine(2)
Console.WriteLine(-2)
Console.WriteLine(2+2)
Console.WriteLine(2-2)
End Sub
End Module
+和-可以是加减运算符,也可以是一元符号运算符。 这取决于实际情况。
Option Strict On
Module Example
Dim a As Byte
Sub Main()
a = 1
Console.WriteLine(-a) ' Prints -1
Console.WriteLine(-(-a)) ' Prints 1
End Sub
End Module
加号可以用来表示我们有一个正数。 但是它通常不被使用。 减号更改值的符号。
Option Strict On
Module Example
Dim a As Byte
Dim b As Byte
Sub Main()
a = 3 * 3
b = 2 + 2
Console.WriteLine(a) ' Prints 9
Console.WriteLine(b) ' Print 4
End Sub
End Module
乘法和加法运算符是二进制运算符的示例。 它们与两个操作数一起使用。
赋值运算符
赋值运算符=
将值赋给变量。 variable
是值的占位符。 在数学中,=
运算符具有不同的含义。 在等式中,=
运算符是一个相等运算符。 等式的左侧等于右侧。
x = 1
Console.WriteLine(x) ' Prints 1
在这里,我们为x
变量分配一个数字。
x = x + 1
Console.WriteLine(x)
先前的表达式在数学上没有意义。 但这在编程中是合法的。 该表达式将x
变量加 1。 右边等于 2,并且 2 分配给x
。
3 = x
此代码示例导致语法错误。 我们无法为字面值分配值。
算术运算符
下表是 Visual Basic 中的算术运算符表。
符号 | 名称 |
---|---|
+ |
加法 |
- |
减法 |
* |
乘法 |
/ |
除法 |
\ |
整数除法 |
Mod |
模数 |
^ |
求幂 |
以下示例显示了算术运算。
Option Strict On
Module Example
Dim a As Byte
Dim b As Byte
Dim c As Byte
Dim add As Byte
Dim sb As Byte
Dim mult As Byte
Dim div As Byte
Sub Main()
a = 10
b = 11
c = 12
add = a + b + c
sb = c - a
mult = a * b
div = CType(c / 3, Byte)
Console.WriteLine(add)
Console.WriteLine(sb)
Console.WriteLine(mult)
Console.WriteLine(div)
End Sub
End Module
在前面的示例中,我们使用加,减,乘和除运算。 这些都是数学所熟悉的。
$ ./arithmetic.exe
33
2
110
4
示例的输出。
接下来,我们将说明正态除法和整数除法之间的区别。
Option Strict On
Module Example
Dim a As Single = 5
Dim b As Single = 2
Dim c As Single
Sub Main()
c = 5 / 2
Console.WriteLine(c)
c = 5 \ 2
Console.WriteLine(c)
End Sub
End Module
在前面的示例中,我们使用普通和整数除法运算符对两个数进行除法。 Visual Basic 有两个不同的除法运算符。
Dim a As Single = 5
我们使用浮点数据类型。
c = 5 / 2
Console.WriteLine(c)
这是“正常”的分割操作。 它返回 2.5,如预期的那样。
c = 5 \ 2
Console.WriteLine(c)
这是整数除法。 此操作的结果始终为整数。 c
变量的值为 2。
$ ./division.exe
2.5
2
division.exe
程序的结果。
我们将提到的最后两个运算符是模运算符和幂运算符。
Console.WriteLine(9 Mod 4) ' Prints 1
Mod
运算符称为模运算符。 它找到一个数除以另一个的余数。 9 Mod 4
,9 模 4 为 1,因为 4 两次乘以 9,余数为 1。例如,当我们要检查素数时,模运算符会很方便。
最后,我们将提到求幂运算符。
Console.WriteLine(9 ^ 2) ' Prints 81
9 ^ 2 = 9 * 9 = 81
连接字符串
在 Visual Basic 中,我们有两个用于字符串连接的运算符。 加号+
运算符和&
和号运算符。
Option Strict On
Module Example
Sub Main()
Console.WriteLine("Return " & "of " & "the king")
Console.WriteLine("Return " + "of " + "the king")
End Sub
End Module
我们使用两个运算符将三个字符串连接在一起。
$ ./concatstrings.exe
Return of the king
Return of the king
这就是我们得到的。 两种情况的结果相同。
布尔运算符
在 Visual Basic 中,我们具有以下逻辑运算符。 布尔运算符也称为逻辑运算符。
符号 | 名称 |
---|---|
And |
逻辑与 |
AndAlso |
短路与 |
Or |
逻辑或 |
OrElse |
短路或 |
Xor |
逻辑异或 |
Not |
否定 |
布尔运算符用于处理真值。
Option Strict On
Module Example
Dim x As Byte = 3
Dim y As Byte = 8
Sub Main()
Console.WriteLine(x = y)
Console.WriteLine(y > x)
If (y > x)
Console.WriteLine("y is greater than x")
End If
End Sub
End Module
许多表达式导致布尔值。 布尔值用于条件语句中。
Console.WriteLine(x = y)
Console.WriteLine(y > x)
关系运算符始终导致布尔值。 这两行显示False
和True
。
If (y > x)
Console.WriteLine("y is greater than x")
End If
仅在满足括号内的条件时才执行If
语句的主体。 x > y
返回True
,因此消息"y
大于x"
被打印到终端。
Option Strict On
Module Example
Dim a As Boolean
Dim b As Boolean
Dim c As Boolean
Dim d As Boolean
Sub Main()
a = (True And True)
b = (True And False)
c = (False And True)
d = (False And False)
Console.WriteLine(a)
Console.WriteLine(b)
Console.WriteLine(c)
Console.WriteLine(d)
End Sub
End Module
示例显示了逻辑And
运算符。 仅当两个操作数均为True
时,它的求值结果为True
。
$ ./andop.exe
True
False
False
False
如果正好一个操作数为True
,则逻辑Xor
运算符的计算结果为True
。
Option Strict On
Module Example
Dim a As Boolean
Dim b As Boolean
Dim c As Boolean
Dim d As Boolean
Sub Main
a = (True Xor True)
b = (True Xor False)
c = (False Xor True)
d = (False Xor False)
Console.WriteLine(a)
Console.WriteLine(b)
Console.WriteLine(c)
Console.WriteLine(d)
End Sub
End Module
如果两个操作数均为True
或均为False
,则逻辑Xor
的计算结果为False
。
$ ./xorop.exe
False
True
True
False
如果两个操作数中的任何一个为True
,则逻辑Or
运算符的计算结果为True
。
Option Strict On
Module Example
Sub Main()
Dim a As Boolean = True Or True
Dim b As Boolean = True Or False
Dim c As Boolean = False Or True
Dim d As Boolean = False Or False
Console.WriteLine(a)
Console.WriteLine(b)
Console.WriteLine(c)
Console.WriteLine(d)
End Sub
End Module
如果运算符的任一侧为 True,则运算结果为 True。
$ ./orop.exe
True
True
True
False
否定运算符Not
设为True False
和False True
。
Option Strict On
Module Example
Sub Main()
Console.WriteLine(Not True)
Console.WriteLine(Not False)
Console.WriteLine(Not (4 < 3))
End Sub
End Module
该示例显示了否定运算符的作用。
$ ./negation.exe
False
True
True
AndAlso
和OrElse
运算符经过短路求值。 短路求值意味着仅当第一个参数不足以确定表达式的值时才求值第二个参数:当And
的第一个参数求值为false
时,总值必须为false
; 并且当Or
的第一个参数计算为true
时,总值必须为true
。 (维基百科)短路求值主要用于提高性能。
一个例子可以使这一点更加清楚。
Option Strict On
Module Example
Sub Main()
Console.WriteLine("Short circuit")
If (one AndAlso two)
Console.WriteLine("Pass")
End If
Console.WriteLine("#############")
If (one And two)
Console.WriteLine("Pass")
End If
End Sub
Function one As Boolean
Console.WriteLine("Inside one")
Return False
End Function
Function two As Boolean
Console.WriteLine("Inside two")
Return True
End Function
End Module
在示例中,我们有两个函数。 函数与子例程不同,返回值。 这是它们之间的主要区别。
If (one AndAlso two)
Console.WriteLine("Pass")
End If
一个函数返回False
。 短路AndAlso
不求值第二功能。 没有必要。 一旦操作数为False
,逻辑结论的结果始终为False
。 控制台上仅打印"Inside one"
。
Console.WriteLine("#############")
If (one And two)
Console.WriteLine("Pass")
End If
在第二种情况下,我们使用And
。 在这种情况下,两个函数都被调用。 即使表达式的结果不是必需的。
$ ./shorcircuit.exe
Short circuit
Inside one
#############
Inside one
Inside two
shorcircuit.exe
程序的结果。
关系运算符
关系运算符用于比较值。 这些运算符总是产生布尔值。
符号 | 含义 |
---|---|
< |
小于 |
<= |
小于或等于 |
> |
大于 |
>= |
大于或等于 |
== |
等于 |
<> |
不等于 |
Is |
比较引用 |
关系运算符也称为比较运算符。
Console.WriteLine(3 < 4) ' Prints True
Console.WriteLine(3 = 4) ' Prints False
Console.WriteLine(4 >= 3) ' Prints True
正如我们已经提到的,关系运算符返回布尔值。 请注意,在 Visual Basic 中,比较运算符为=
。 不像受 C 和 C 影响的语言那样==
。
注意,关系运算符不限于数字。 我们也可以将它们用于其他对象。 尽管它们可能并不总是有意义的。
Option Strict On
Module Example
Sub Main()
Console.WriteLine("six" = "six") ' Prints True
' Console.WriteLine("a" > 6) this would throw
' an exception
Console.WriteLine("a" < "b") ' Prints True
End Sub
End Module
我们也可以比较字符串对象。 字符串上下文中的比较运算符比较字符的排序顺序。
Console.WriteLine("a" < "b") ' Prints True
这里到底发生了什么? 计算机不知道字符或字符串。 对于他们来说,一切都只是数字。 字符是存储在特定表中的特殊数字。 类似于 ASCII。
Option Strict On
Module Example
Sub Main()
Console.WriteLine("a" < "b")
Console.WriteLine("a is: {0}", Asc("a"))
Console.WriteLine("b is: {0}", Asc("b"))
End Sub
End Module
在内部,a 和 b 字符是数字。 因此,当我们比较两个字符时,我们将比较它们的存储数字。 内置的Asc
函数返回单个字符的 ASCII 值。
$ ./compare.exe
True
a is: 97
b is: 98
实际上,我们将 97 和 98 这两个数字进行比较。
Console.WriteLine("ab" > "aa") ' Prints True
假设我们有一个包含更多字符的字符串。 如果前几个字符相等,我们将比较下一个字符。 在我们的情况下,第二个位置的 b 字符的值比 a 字符大。 这就是为什么"ab"
字符串大于"aa"
字符串的原因。 当然,以这种方式比较字符串没有多大意义。 但这在技术上是可能的。
最后,我们将提到Is
运算符。 运算符检查两个对象引用是否引用同一对象。 它不执行值比较。
Option Strict On
Module Example
Sub Main()
Dim o1 As Object = New Object
Dim o2 As Object = New Object
Dim o3 As Object
o3 = o2
Console.WriteLine(o1 Is o2)
Console.WriteLine(o3 Is o2)
End Sub
End Module
我们创建三个对象,并将它们与Is
运算符进行比较。
Dim o1 As Object = New Object
Dim o2 As Object = New Object
我们声明并初始化两个Object
实例。 Object
类是.NET 框架中所有类的基类。 我们将在以后更详细地描述它。
Dim o3 As Object
仅声明第三个变量。
o3 = o2
o3
现在引用o2
对象。 它们是对同一对象的两个引用。
Console.WriteLine(o1 Is o2)
Console.WriteLine(o3 Is o2)
在第一种情况下,我们得到False
。 o1
和o2
是两个不同的对象。 在第二种情况下,我们得到True
。 o3
和o2
指的是同一对象。
按位运算符
小数对人类是自然的。 二进制数是计算机固有的。 二进制,八进制,十进制或十六进制符号仅是相同数字的符号。 按位运算符使用二进制数的位。 像 Visual Basic 这样的高级语言很少使用按位运算符。
符号 | 含义 |
---|---|
Not |
按位取反 |
Xor |
按位异或 |
And |
按位与 |
Or |
按位或 |
按位取反运算符分别将 1 更改为 0,将 0 更改为 1。
Console.WriteLine(Not 7) ' Prints -8
Console.WriteLine(Not -8) ' Prints 7
运算符恢复数字 7 的所有位。这些位之一还确定数字是否为负。 如果我们再一次对所有位取反,我们将再次得到 7。
按位,运算符在两个数字之间进行逐位比较。 仅当操作数中的两个对应位均为 1 时,位位置的结果才为 1。
00110
And 00011
= 00010
第一个数字是二进制符号 6,第二个数字是 3,结果是 2。
Console.WriteLine(6 And 3) ' Prints 2
Console.WriteLine(3 And 6) ' Prints 2
按位或运算符在两个数字之间进行逐位比较。 如果操作数中的任何对应位为 1,则位位置的结果为 1。
00110
Or 00011
= 00111
结果为00110
或十进制 7。
Console.WriteLine(6 Or 3) ' Prints 7
Console.WriteLine(3 Or 6) ' Prints 7
按位互斥或运算符在两个数字之间进行逐位比较。 如果操作数中对应位中的一个或另一个(但不是全部)为 1,则位位置的结果为 1。
00110
Xor 00011
= 00101
结果为00101
或十进制 5。
Console.WriteLine(6 Xor 3) ' Prints 5
复合赋值运算符
复合赋值运算符由两个运算符组成。 他们是速记员。
Option Strict On
Module Example
Dim a As Integer
Sub Main
a = 1
a = a + 1
Console.WriteLine(a) ' Prints 2
a += 1
Console.WriteLine(a) ' Prints 3
End Sub
End Module
+=
复合运算符是这些速记运算符之一。 它们比完整的表达式可读性差,但是经验丰富的程序员经常使用它们。
其他复合运算符是:
-= *= \= /= &= ^=
运算符优先级
运算符优先级告诉我们首先求值哪个运算符。 优先级对于避免表达式中的歧义是必要的。
以下表达式 28 或 40 的结果是什么?
3 + 5 * 5
像数学中一样,乘法运算符的优先级高于加法运算符。 结果是 28。
(3 + 5) * 5
要更改求值的顺序,可以使用括号。 括号内的表达式始终首先被求值。
下面的列表显示了按优先级排序的常见 Visual Basic 运算符(优先级最高):
运算符 | 描述 |
---|---|
^ |
求幂 |
+ - |
一元加减 |
* / |
乘法,浮点除法 |
\ |
整数除法 |
Mod |
模数 |
+ - |
加法,减法,字符串连接 |
& |
字符串连接 |
<< >> |
算术移位 |
= <> < > >= <= Is IsNot Like TypeOf Is |
所有比较运算符 |
Not |
否定 |
And AndAlso |
与 |
Or OrElse |
或 |
Xor |
异或 |
列表中同一行上的运算符具有相同的优先级。
Option Strict On
Module Example
Sub Main()
Console.WriteLine(3 + 5 * 5)
Console.WriteLine((3 + 5) * 5)
Console.WriteLine(Not True Or True)
Console.WriteLine(Not (True Or True))
End Sub
End Module
在此代码示例中,我们显示一些常见的表达式。 每个表达式的结果取决于优先级。
Console.WriteLine(3 + 5 * 5)
该行打印 28。乘法运算符的优先级高于加法。 首先计算5*5
的乘积。 然后添加 3。
Console.WriteLine(Not True Or True)
在这种情况下,否定运算符具有更高的优先级。 首先,第一个True
值被否定为False
,然后Or
运算符组合False
和True
,最后得到True
。
$ ./precedence.exe
28
40
True
False
关联性
有时,优先级不能令人满意地确定表达式的结果。 还有另一个规则称为关联性。 运算符的关联性确定优先级与相同的运算符的求值顺序。
9 / 3 * 3
这种表达的结果是什么? 9 还是 1? 乘法,删除和模运算符从左到右关联。 因此,该表达式的计算方式为:(9 / 3) * 3
,结果为 9。
算术,布尔,关系和按位运算符都是左右关联的。
另一方面,赋值运算符是正确关联的。
a = b = c = d = 0
Console.WriteLine("{0} {1} {2} {3}", a, b, c, d) ' Prints 0 0 0 0
如果关联从左到右,则以前的表达式将不可能。
复合赋值运算符从右到左关联。
j = 0
j *= 3 + 1
Console.WriteLine(j)
您可能期望结果为 1。但是实际结果为 0。由于有关联性。 首先求值右边的表达式,然后应用复合赋值运算符。
AddressOf
运算符
AddressOf
运算符创建一个指向另一个函数的函数委托。 委托是类型安全的函数指针,它们用于调用其他对象的方法。
Option Strict On
Module Example
Delegate Sub Display
Dim msg As New Display(AddressOf Message1)
Sub Main()
msg.Invoke()
msg = New Display(AddressOf Message2)
msg.Invoke()
End Sub
Sub Message1()
Console.WriteLine("This is message 1")
End Sub
Sub Message2()
Console.WriteLine("This is message 2")
End Sub
End Module
在代码示例中,我们使用AddressOf
运算符指向两个不同的子例程。
Delegate Sub Display
我们需要声明一个委托。
Dim msg As New Display(AddressOf Message1)
委托使用AddressOf
运算符获取子例程的地址。 现在,我们有一个指向Message1()
子例程的类型安全指针。
msg.Invoke()
Invoke()
方法调用委托指向的方法。
msg = New Display(AddressOf Message2)
msg.Invoke()
现在,我们为委托人提供另一个子例程的地址。
$ ./addressof.exe
This is message 1
This is message 2
两条消息都打印到控制台。
在 Visual Basic 教程的这一部分中,我们介绍了运算符。
控制流
在 Visual Basic 教程的这一部分中,我们将讨论流控制。 我们将定义几个关键字,使我们能够控制 Visual Basic 程序的流程。
在 Visual Basic 语言中,有几个关键字可用于更改程序的流程。 当程序运行时,语句从源文件的顶部到底部执行。 逐一。 可以通过特定的关键字更改此流程。 语句可以执行多次。 一些语句称为条件语句。 仅在满足特定条件时才执行它们。
If
语句
If
语句具有以下一般形式:
If (expression)
statement
End If
If
关键字用于检查表达式是否为真。 如果为true
,则执行一条语句。 该语句可以是单个语句或复合语句。 复合语句由If/End If
块包围的多个语句组成。
Option Strict On
Module Example
Dim num As Byte = 31
Sub Main()
If (num > 0)
Console.WriteLine("num variable is positive")
End If
End Sub
End Module
我们有一个num
变量。 它被分配为 31。If
关键字检查布尔表达式。 表达式放在方括号之间。 31 > 0
为true
,因此将执行块内的语句。
$ ./ifstatement.exe
num variable is positive
满足条件,并将消息写入控制台。
Option Strict On
Module Example
Dim num As Byte = 31
Sub Main()
If (num > 0)
Console.WriteLine("num variable is positive")
Console.WriteLine("num variable equals {0}", num)
End If
End Sub
End Module
可以在由If
和End If
关键字创建的块内执行更多语句。
我们可以使用Else
关键字来创建一个简单的分支。 如果If
关键字后方括号内的表达式的值为假,则将自动执行Else
关键字后方的语句。
Option Strict On
Module Example
Dim sex As String
Sub Main()
sex = "female"
If (sex = "male")
Console.WriteLine("It is a boy")
Else
Console.WriteLine("It is a girl")
End If
End Sub
End Module
我们有性别变量。 它具有"female"
字符串。 布尔表达式的计算结果为false
,我们在控制台中得到"It is a girl"
。
$ ./branch.exe
It is a girl
我们可以使用Else If
关键字创建多个分支。 仅当不满足先前条件时,Else If
关键字才会测试其他条件。 请注意,我们可以在测试中使用多个Else If
关键字。
Option Strict On
Module Example
Dim a As Byte = 0
Sub Main()
If (a < 0)
Console.WriteLine("a is negative")
Else If (a = 0)
Console.WriteLine("a equals to zero")
Else
Console.WriteLine("a is a positive number")
End If
End Sub
End Module
我们有一个数值变量,并测试它是否为负数或正数或等于零。 第一个表达式的计算结果为false
。 满足第二个条件。 程序在控制台上输出a equals to zero
。 分支的其余部分将被跳过。
Select
语句
Select
语句是选择控制流语句。 它允许变量或表达式的值通过多路分支控制程序执行的流程。 与使用If
和Else If
语句的组合相比,它以更简单的方式创建多个分支。
我们有一个变量或一个表达式。 Select
关键字用于根据值列表测试变量或表达式中的值。 值列表用Case
关键字显示。 如果值匹配,则执行Case
之后的语句。 有一个可选的Case Else
语句。 如果找不到其他匹配项,则执行该命令。
Option Strict On
Module Example
Dim domain As String
Sub Main()
domain = Console.ReadLine()
Select domain
Case "us"
Console.WriteLine("United States")
Case "de"
Console.WriteLine("Germany")
Case "sk"
Console.WriteLine("Slovakia")
Case "hu"
Console.WriteLine("Hungary")
Case Else
Console.WriteLine("Unknown")
End Select
End Sub
End Module
在我们的程序中,我们有一个域变量。 我们从命令行读取变量的值。 我们使用Case
语句测试变量的值。 有几种选择。 例如,如果该值等于"us"
,则将"United States"
字符串打印到控制台。
$ ./selectcase.exe
hu
Hungary
我们在控制台输入了"hu"
字符串,程序以"Hungary"
作为响应。
使用Select
关键字可以验证一系列数字个案。
Option Strict On
Module Example
Dim age As Byte
Sub Main()
Try
age = Console.ReadLine()
Catch
Console.WriteLine("Invalid value")
End
End Try
Select age
Case 0 To 21
Console.WriteLine("Junior")
Case 22 To 60
Console.WriteLine("Adult")
Case Else
Console.WriteLine("Senior")
End Select
End Sub
End Module
前面的程序使用数值范围来标识一个人的年龄组。
Try
age = Console.ReadLine()
Catch
Console.WriteLine("Invalid value")
End
End Try
从控制台读取一个值。 我们只能使用数值数据。 Try
,Catch
和End Try
关键字用于异常处理。 如果引发异常,则执行Catch
关键字之后的语句。 End
语句终止程序。
Case 0 To 21
Console.WriteLine("Junior")
在这里,我们指定一个值范围。 如果用户输入的值在 0 到 21 之间(包括 0 和 21),则程序将在控制台上打印"Junior"
。
$ ./agerange.exe
43
Adult
我们输入了 43,程序以Adult
字符串作为响应。
While
语句
While
语句是一个控制流语句,它允许根据给定的布尔条件重复执行代码。
这是While
循环的一般形式:
While (expression):
statement
End While
While
关键字在While
和End While
关键字包围的块内执行语句。 每次将表达式求值为true
时都会执行这些语句。
Option Strict On
Module Example
Sub Main()
Dim i As Integer = 0
Dim sum As Integer = 0
While i < 10
i = i + 1
sum += i
End While
Console.WriteLine(sum)
End Sub
End Module
在代码示例中,从一系列数字计算值的总和。
While
循环包含三个部分:初始化,测试和更新。 语句的每次执行都称为循环。
Dim i As Integer = 0
我们启动i
变量。 它用作计数器。
While i < 10
...
End While
While
关键字后面的表达式是第二阶段,即测试。 执行主体中的语句,直到表达式的计算结果为false
。
i = i + 1
While
循环的最后一个第三阶段。 正在更新。 我们增加计数器。 请注意,对While
循环的不正确处理可能会导致循环不断。
可以至少运行一次该语句。 即使不满足条件。 为此,我们可以使用Do
和Loop While
关键字。
Option Strict On
Module Example
Sub Main()
Dim count As Integer = 0
Do
Console.WriteLine(count)
Loop While (count <> 0)
End Sub
End Module
首先执行迭代,然后求值真值表达式。
For Next
语句
如果在启动循环之前知道周期数,则可以使用For Next
语句。 在此构造中,我们声明一个计数器变量,该变量在每次循环重复期间都会自动增加或减少值。
Option Strict On
Module Example
Sub Main()
For i As Integer = 0 To 9
Console.WriteLine(i)
Next
End Sub
End Module
在此示例中,我们将数字0..9
打印到控制台。
For i As Integer = 0 To 9
Console.WriteLine(i)
Next
我们将计数器 i 初始化为零。 Next
语句将计数器增加一,直到计数器等于 9。
Visual Basic 具有可选的Step
关键字。 它控制计数器变量如何增加或减少。
Option Strict On
Module Example
Sub Main()
For i As Integer = 9 To 0 Step -1
Console.WriteLine(i)
Next
End Sub
End Module
在上面的示例中,我们以相反的顺序打印数字 0..9。
For i As Integer = 9 To 0 Step -1
Console.WriteLine(i)
Next
该步骤也可以是负数。 我们将计数器初始化为 9。每次迭代,计数器都将减小步长值。
For Each
语句
For Each
构造简化了遍历数据集合的过程。 它没有明确的计数器。 For Each
语句一个接一个地遍历数组或集合,并将当前值复制到构造中定义的变量中。
Option Strict On
Module Example
Sub Main()
Dim planets() As String = { "Mercury", "Venus", _
"Earth", "Mars", "Jupiter", "Saturn", _
"Uranus", "Neptune" }
For Each planet As String In planets
Console.WriteLine(planet)
Next
End Sub
End Module
在此示例中,我们使用For Each
语句遍历一系列行星。
For Each planet As String In planets
Console.WriteLine(planet)
Next
For Each
语句的用法很简单。 planets
是我们迭代通过的数组。 planet
是一个临时变量,具有数组中的当前值。 For Each
语句遍历所有行星并将它们打印到控制台。
$ ./planets.exe
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
运行上面的 Visual Basic 程序将给出此输出。
Exit
,Continue
语句
Exit
语句可用于终止由While
,For
或Select
语句定义的块。
Option Strict On
Module Example
Dim val As Integer
Sub Main
While (True)
val = CType((30 * Rnd), Integer) + 1
Console.Write(val.ToString & " ")
If (val = 22)
Exit While
End If
End While
Console.Write(vbNewLine)
End Sub
End Module
我们定义了一个无限的While
循环。 只有一种方法可以跳出这样的循环。 我们必须使用Exit While
语句。 我们从 1 到 30 中选择一个随机值。我们打印该值。 如果该值等于 22,则结束无穷的while
循环。
$ ./exitstm.exe
30 12 13 20 19 4 2 9 6 9 22
我们可能会得到这样的东西。
Continue
语句用于跳过循环的一部分,并继续循环的下一个迭代。 它可以与Do
,For
和While
语句结合使用。
在下面的示例中,我们将打印一个数字列表,这些数字不能除以 2 而没有余数。
Option Strict On
Module Example
Dim num As Integer = 0
Sub Main()
While (num < 1000)
num = num + 1
If ((num Mod 2) = 0)
Continue While
End If
Console.Write(num.ToString() + " ")
End While
Console.Write(vbNewLine)
End Sub
End Module
我们使用While
循环遍历数字1..999
。
If ((num Mod 2) = 0)
Continue While
End If
如果表达式num Mod 2
返回 0,则可以将所讨论的数字除以 2。执行Continue
语句,并跳过循环的其余部分。 在我们的例子中,循环的最后一条语句将被跳过,并且数字不会输出到控制台。 下一个迭代开始。
在 Visual Basic 教程的这一部分中,我们讨论的是控制流结构。
{% raw %}
Visual Basic 数组
在 Visual Basic 编程教程的这一部分中,我们将介绍数组。 我们将初始化数组并从中读取数据。
数组是数据的集合。 变量一次只能容纳一项。 数组可以容纳多个项目。 这些项目称为数组的元素。 数组存储相同数据类型的数据。 每个元素都可以由索引引用。 数组从零开始。 第一个元素的索引为零。
集合具有相似的目的。 它们比数组更强大。 它们将在后面描述。
数组用于存储我们应用的数据。 我们声明数组为某种数据类型。 我们指定它们的长度。 我们用数据初始化数组。 我们有几种使用数组的方法。 我们可以修改元素,对其进行排序,复制或搜索。
初始化数组
有几种方法可以在 Visual Basic 中初始化数组。
Option Strict On
Module Example
Sub Main()
Dim array(5) As Integer
array(0) = 3
array(1) = 2
array(2) = 1
array(3) = 5
array(4) = 6
For i As Integer = 0 To array.Length-1
Console.WriteLine(array(i))
Next
End Sub
End Module
我们声明并初始化一个数值数组。 数组的内容将打印到控制台。
Dim array(5) As Integer
在这里,我们声明一个包含五个元素的数组。 所有元素都是整数。
array(0) = 3
array(1) = 2
...
我们用一些数据初始化数组。 这是分配初始化。 索引在括号中。 数字 3 将成为数组的第一个元素,数字 2 将成为第二个元素。
For i As Integer = 0 To array.Length-1
Console.WriteLine(array(i))
Next
我们遍历数组并打印其元素。 数组具有Length
属性,该属性给出数组中元素的数量。 由于数组基于零,因此索引为0..length-1
。
我们可以在一个语句中声明并初始化一个数组。
Option Strict On
Module Example
Sub Main()
Dim array() As Integer = { _
2, 4, 5, 6, 7, 3, 2 }
For Each i As Integer In array
Console.WriteLine(i)
Next
End Sub
End Module
这是先前程序的修改版本。
Dim array() As Integer = { _
2, 4, 5, 6, 7, 3, 2 }
一步就声明并初始化一个数组。 元素在大括号中指定。 我们没有指定数组的长度。 编译器将为我们完成此任务。
For Each i As Integer In array
Console.WriteLine(i)
Next
我们使用For Each
关键字遍历数组并打印其内容。
数组的界限
Visual Basic 具有两个用于获取数组边界的函数。 LBound()
函数返回数组指定维的最低可用下标。 UBound()
函数返回数组指定维的最高可用下标。 到目前为止,我们已经处理了一维数组。
Option Strict On
Module Example
Dim n1 As Integer
Dim n2 As Integer
Sub Main()
Dim names() As String = { "Jane", "Lucy", _
"Timea", "Beky", "Lenka"}
n1 = LBound(names)
n2 = UBound(names)
Console.WriteLine(n1)
Console.WriteLine(n2)
For i As Integer = n1 To n2
Console.WriteLine(names(i))
Next
End Sub
End Module
我们有一系列名称。 我们计算并使用该数组的上下边界。
n1 = LBound(names)
n2 = UBound(names)
在名称数组中,n1
是最低索引,n2
是最高索引。
For i As Integer = n1 To n2
Console.WriteLine(names(i))
Next
我们使用数组的上下边界检查数组。
$ ./bounds.exe
0
4
Jane
Lucy
Timea
Beky
Lenka
示例的输出。
数组大小
到目前为止,我们已经处理了一维数组。 指定元素所需的索引数称为数组的维或等级。
我们将使用二维数组。
Option Strict On
Module Example
Sub Main()
Dim numbers(,) As Integer = { {2, 1}, {3, 5}, _
{4, 4}, {7, 2}, {0, 0} }
For i As Integer = 0 To UBound(numbers, 1)
For j As Integer = 0 To UBound(numbers, 2)
Console.Write(CStr(numbers(i, j)) + " ")
Next j
Console.Write(vbNewLine)
Next i
End Sub
End Module
如果我们需要两个索引来访问数组中的元素,那么我们将拥有一个二维数组。
Dim numbers(,) As Integer = { {2, 1}, {3, 5}, _
{4, 4}, {7, 2}, {0, 0} }
我们在一个语句中声明并初始化一个二维数组。 请注意数组名称后面括号内的逗号。
For i As Integer = 0 To UBound(numbers, 1)
For j As Integer = 0 To UBound(numbers, 2)
Console.Write(CStr(numbers(i, j)) + " ")
Next j
Console.Write(vbNewLine)
Next i
我们需要两个循环才能从二维数组中获取数据。 UBound()
函数具有可选的第二个参数rank
。 这是我们检索最高索引的维度。 如果省略等级,则假定为 1 维。
$ ./twodimensions.exe
2 1
3 5
4 4
7 2
0 0
代码示例的输出。
接下来,我们将处理三维数组。
Option Strict On
Module Example
Sub Main()
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim nums(,,) As Integer = { _
{{12, 2, 8}}, _
{{14, 5, 2}}, _
{{3, 26, 9}}, _
{{4, 11, 2}} _
}
For i = 0 To UBound(nums, 1)
For j = 0 To UBound(nums, 2)
For k = 0 To UBound(nums, 3)
Console.Write(CStr(nums(i, j, k)) + " ")
Next k
Next j
Console.Write(vbNewLine)
Next i
End Sub
End Module
我们有一个数字三维数组。 同样,我们用数字初始化数组并将其打印到终端。
Dim nums(,,) As Integer = { _
{{12, 2, 8}}, _
{{14, 5, 2}}, _
{{3, 26, 9}}, _
{{4, 11, 2}} _
}
左括号和右括号之间还有另一个逗号。
For k = 0 To UBound(nums, 3)
Console.Write(CStr(nums(i, j, k)) + " ")
Next k
该循环经过三维。 我们使用三个索引从数组中检索值。
$ ./3darray.exe
12 2 8
14 5 2
3 26 9
4 11 2
我们将三维数组的内容打印到控制台。
有一个Rank()
函数,它给出数组的维数。
Option Strict On
Module Example
Sub Main()
Dim array1() As Integer = {1, 2}
Dim array2(,) As Integer = { { 1 }, { 2 } }
Dim array3(, ,) As Integer = { { { 1, 2 }, { 2, 1 } } }
Console.WriteLine(array1.Rank())
Console.WriteLine(array2.Rank())
Console.WriteLine(array3.Rank())
End Sub
End Module
我们有三个数组。 我们使用Rank()
函数来获取每个大小的大小。
Console.WriteLine(array1.Rank())
在这里,我们获得第一个数组的排名。
锯齿状数组
具有相同大小元素的数组称为矩形数组。 相反,具有不同大小元素的数组称为锯齿状数组。 锯齿状数组的声明和初始化方式不同。
Option Strict On
Module Example
Sub Main()
Dim jagged As Integer()() = New Integer(4)() {}
jagged(0) = New Integer() {1}
jagged(1) = New Integer() {3, 4}
jagged(2) = New Integer() {5, 6, 7}
jagged(3) = New Integer() {5, 6}
jagged(4) = New Integer() {9}
For i As Integer = 0 To jagged.GetUpperBound(0)
For j As Integer = 0 To jagged(i).GetUpperBound(0)
Console.Write(jagged(i)(j) & " ")
Next
Console.Write(vbNewLine)
Next
End Sub
End Module
这是一个锯齿状数组的示例。
Dim jagged As Integer()() = New Integer(4)() {}
这是锯齿状数组的声明。 我们有一个数组数组。 更具体地说,我们声明了一个数组,该数组具有五个整数数据类型的数组。
jagged(0) = New Integer() {1}
jagged(1) = New Integer() {3, 4}
...
每个数组必须单独初始化。
Console.Write(jagged(i)(j) & " ")
与矩形数组不同,每个索引都用括号括起来。
数组方法
有多种使用数组的方法。 这些方法可用于检索,修改数据,排序,复制,搜索数据。 我们使用的这些方法是Array
类的静态方法或数组对象的成员方法。
Option Strict On
Module Example
Sub Main()
Dim names() As String = {"Jane", "Frank", "Alice", "Tom" }
Array.Sort(names)
For Each el As String In names
Console.Write(el + " ")
Next
Console.Write(vbNewLine)
Array.Reverse(names)
For Each el As String In names
Console.Write(el + " ")
Next
Console.Write(vbNewLine)
End Sub
End Module
在此示例中,我们对数据进行排序。
Dim names() As String = {"Jane", "Frank", "Alice", "Tom" }
我们有一个字符串数组。
Array.Sort(names)
Sort()
方法按字母顺序对数据进行排序。
Array.Reverse(names)
Reverse()
方法反转整个一维数组中元素的顺序。
$ ./sorting.exe
Alice Frank Jane Tom
Tom Jane Frank Alice
我们已经按升序和降序对名称进行了排序。
以下示例使用SeValue()
,GetValue()
,IndexOf()
,Copy()
和Clear()
方法。
Option Strict On
Module Example
Dim names() As String = {"Jane", "Frank", "Alice", "Tom" }
Dim girls(0 To 3) As String
Sub Main()
names.SetValue("Beky", 1)
names.SetValue("Erzebeth", 3)
Console.WriteLine(names.GetValue(1))
Console.WriteLine(names.GetValue(3))
Console.WriteLine(Array.IndexOf(names, "Erzebeth"))
Array.Copy(names, girls, names.Length)
For Each el As String In girls
Console.Write(el + " ")
Next
Console.Write(vbNewLine)
Array.Clear(names, 0, 2)
For Each el As String In names
Console.Write(el + " ")
Next
Console.Write(vbNewLine)
End Sub
End Module
本示例介绍了其他方法。
Dim girls(0 To 3) As String
声明数组的另一种方法。
names.SetValue("Beky", 1)
names.SetValue("Erzebeth", 3)
SetValue()
为数组中的特定索引设置一个值。
Console.WriteLine(names.GetValue(1))
Console.WriteLine(names.GetValue(3))
我们使用GetValue()
方法从数组中检索值。
Console.WriteLine(Array.IndexOf(names, "Erzebeth"))
IndexOf()
方法返回首次出现特定值的索引。
Array.Copy(names, girls, names.Length)
Copy()
方法将值从源数组复制到目标数组。 第一个参数是源数组,第二个参数是目标数组。 第三个参数是长度; 它指定要复制的元素数。
Array.Clear(names, 0, 2)
Clear()
方法从数组中清除元素。 它需要三个参数,即数组,起始索引和要从索引中清除的元素数。
在 Visual Basic 教程的这一部分中,我们使用了数组。 我们描述了各种类型的数组和使用它们的方法。
{% endraw %}
Visual Basic 中的过程&函数
在本部分中,您将学习 Visual Basic 过程&函数。
我们使用过程和函数来创建模块化程序。 Visual Basic 语句分组在由Sub
,Function
和匹配的End
语句包围的块中。 两者之间的区别在于函数返回值,过程不返回值。
过程和函数是较大程序中的一段代码。 他们执行特定任务。 使用过程和函数的优点是:
- 减少代码重复
- 将复杂的问题分解成更简单的部分
- 提高代码的清晰度
- 重用代码
- 信息隐藏
过程
过程是Sub
,End Sub
语句内的 Visual Basic 语句块。 过程不返回值。
Option Strict On
Module Example
Sub Main()
SimpleProcedure()
End Sub
Sub SimpleProcedure()
Console.WriteLine("Simple procedure")
End Sub
End Module
此示例显示了过程的基本用法。 在我们的程序中,我们有两个过程。 Main()
过程和用户定义的SimpleProcedure()
。 众所周知,Main()
过程是 Visual Basic 程序的入口点。
SimpleProcedure()
每个过程都有一个名称。 在Main()
过程中,我们调用了用户定义的SimpleProcedure()
过程。
Sub SimpleProcedure()
Console.WriteLine("Simple procedure")
End Sub
程序在Main()
程序之外定义。 过程名称位于Sub
语句之后。 当我们在 Visual Basic 程序中调用过程时,该过程将得到控制。 执行该过程块内的语句。
过程可以采用可选参数。
Option Strict On
Module Example
Sub Main()
Dim x As Integer = 55
Dim y As Integer = 32
Addition(x, y)
End Sub
Sub Addition(ByVal k As Integer, ByVal l As Integer)
Console.WriteLine(k+l)
End Sub
End Module
在上面的示例中,我们将一些值传递给Addition()
过程。
Addition(x, y)
在这里,我们将调用Addition()
过程并将两个参数传递给它。 这些参数是两个Integer
值。
Sub Addition(ByVal k As Integer, ByVal l As Integer)
Console.WriteLine(k+l)
End Sub
我们定义了过程签名。 过程签名是一种描述参数和参数类型的方法,通过该方法可以对函数进行合法调用。 它包含过程的名称,其参数及其类型,对于函数,还包含返回值。 ByVal
关键字指定我们如何将值传递给过程。 在我们的例子中,该过程获得两个数值 55 和 32。将这些数字相加并将结果打印到控制台。
函数
函数是Function
,End Function
语句内的 Visual Basic 语句块。 函数返回值。
有两种基本类型的函数。 内置函数和用户定义的函数。 内置函数是 Visual Basic 语言的一部分。 有各种数学,字符串或转换函数。
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
在前面的示例中,我们使用两个数学函数和一个字符串函数。 内置函数可帮助程序员完成一些常见任务。
在下面的示例中,我们有一个用户定义的函数。
Option Strict On
Module Example
Dim x As Integer = 55
Dim y As Integer = 32
Dim result As Integer
Sub Main()
result = Addition(x, y)
Console.WriteLine(Addition(x, y))
End Sub
Function Addition(ByVal k As Integer, _
ByVal l As Integer) As Integer
Return k+l
End Function
End Module
两个值传递给该函数。 我们将这两个值相加,然后将结果返回到Main()
函数。
result = Addition(x, y)
调用加法函数。 该函数返回结果,并将该结果分配给结果变量。
Function Addition(ByVal k As Integer, _
ByVal l As Integer) As Integer
Return k+l
End Function
这是加法函数签名及其主体。 它还包括用于返回值的返回数据类型。 在我们的例子中是Integer
。 使用Return
关键字将值返回给调用方。
按值传递参数,按引用传递
Visual Basic 支持将参数传递给函数的两种方法。 按值和引用。 为此,我们有两个关键字。 ByVal
和ByRef
。 当我们按值传递参数时,该函数仅适用于值的副本。 当我们处理大量数据时,这可能会导致性能开销。
当我们通过引用传递值时,该函数会收到对实际值的引用。 修改后,原始值会受到影响。 这种传递值的方式更加节省时间和空间。 另一方面,它更容易出错。
我们应该使用哪种方式传递参数? 这取决于实际情况。 假设我们有一组数据,即员工工资。 如果我们要计算数据的某些统计信息,则无需修改它们。 我们通过值。 如果我们处理大量数据,并且计算速度至关重要,则可以引用。 如果我们要修改数据,例如进行一些减薪或加薪,我们可以引用一下。
以下两个示例涵盖了这两个概念。
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
变量之间交换数字。 原始变量不受影响。
Dim a As Byte = 4
Dim b As Byte = 7
最初,这两个变量被启动。
Swap(a, b)
我们称为Swap()
过程。 该过程将a
和b
变量作为参数。
temp = a
a = b
b = temp
在Swap()
过程中,我们更改了值。 请注意,a
和b
变量是在本地定义的。 它们仅在Swap()
程序内有效。
$ ./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()
过程中更改。
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()
过程将更改原始值。
Sub Swap(ByRef a As Byte, ByRef b As Byte)
...
End Sub
现在,我们使用ByRef
关键字来指示我们通过引用传递参数。
$ ./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()
过程确实改变了变量的值。
递归
在数学和计算机科学中,递归是一种定义函数的方法,其中所定义的函数在其自己的定义内应用。 (维基百科)换句话说,递归函数调用自身以完成其工作。 递归是解决许多编程任务的一种广泛使用的方法。
一个典型的例子是阶乘的计算。
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
在此代码示例中,我们计算两个数字的阶乘。
Return n * Factorial(n-1)
在阶乘函数的主体内部,我们使用经过修改的参数调用阶乘函数。 该函数调用自身。
$ ./recursion.exe
24
3628800
这些就是结果。
模块作用域,程序作用域
作用域是可以引用变量的作用域。 在过程内部声明的变量具有过程作用域。 仅在此特定过程中有效。 在模块内部声明的变量具有模块作用域。 它在模块中的任何地方都有效。
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
具有程序作用域。
Dim a As Byte = 2
变量a
在两个过程之外的 Example 模块内部声明。 在两个过程中均有效。
Sub Main()
Dim b As Byte = 3
...
End Sub
变量b
在Main()
过程中声明。 仅在此有效。 在第二个过程中无效。
Sub SimpleProcedure()
Console.WriteLine(a)
' Console.WriteLine(b)
End Sub
注释打印b
变量的语句。 如果我们取消注释该行,则该示例将无法编译。
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()
过程中声明的变量将覆盖在模块作用域中声明的变量。
$ ./scope2.exe
3
输出。
静态变量
静态变量是已静态分配的变量,其生存期遍及整个程序运行。 (维基百科)默认的局部变量在函数的连续调用中不会保留其值。
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 次。 但是,对于函数的每次调用都会启动非静态变量。
$ ./nonstatic.exe
1
在 5 个函数调用之后,x
变量等于 1。
首次调用函数时,静态变量仅启动一次。 之后,他们保留自己的值。
Option Strict On
Module Example
Sub Main()
StaticFunction()
StaticFunction()
StaticFunction()
StaticFunction()
Console.WriteLine(StaticFunction)
End Sub
Function StaticFunction() As Integer
Dim Static x As Integer = 0
x += 1
Return x
End Function
End Module
连续 5 次通话后,x
等于 5。
Dim Static x As Byte = 0
静态变量是使用Static
关键字创建的。
$ ./static.exe
5
运行示例。
在 Visual Basic 教程的这一部分中,我们介绍了过程和函数。
在 Visual Basic 中组织代码
在 Visual Basic 教程的这一部分中,我们将展示如何组织代码。 我们将介绍模块,过程和名称空间以及作用域。
Visual Basic 语句被组织为块,模块,类和名称空间。 这有助于使代码更具可维护性和鲁棒性。 正确的代码组织可以防止在代码中出错。
Visual Basic 程序的基本构建块是:
- 程序集
- 命名空间
- 模块
- 类
- 过程和函数
- 块
- 语句
程序集是 DLL 或 exe 文件。 程序集是用于部署,版本控制和安全性的已编译代码库。 名称空间是提供项目上下文的抽象容器。 模块是在其整个命名空间中可用的引用类型。 类是 OOP 程序的基本构建块。 过程是为执行特定任务而创建的程序的一个单元。 块是由If
或While
之类的某些关键字提供的 Visual Basic 语句的最低级别组织。 语句是 Visual Basic 程序中的原子,是代码的最小单位。
与该主题密切相关的是变量的作用域和持续时间。 作用域是声明的变量的可见性。
作用域 | 描述 |
---|---|
块作用域 | 仅在声明它的代码块中可用 |
过程作用域 | 在声明它的过程中可用 |
模块作用域 | 可用于声明该模块的模块,类或结构中的所有代码 |
命名空间作用域 | 可用于声明它的名称空间中的所有代码 |
变量的生命周期是变量保存值的时间段。 只要过程正在执行,局部变量就存在。 之后,它们将不再可用。 但是,如果我们将变量声明为静态变量,则该过程终止后该变量将继续存在。 在应用的生存期内,存在模块,共享和实例变量。
基本例子
首先,我们介绍一些基本知识。
Option Strict On
Module Example
Sub Main()
Console.WriteLine("Simple example")
End Sub
End Module
在此示例中,我们有一个名为Example
的模块。 在示例内部,我们有一个Main()
子例程。 将一些消息打印到控制台的语句位于Main()
过程中。 事件最简单的 Visual Basic 程序必须正确组织。
Option Strict On
Public Class Example
Public Shared Sub Main()
Console.WriteLine("Simple example")
End Sub
End Class
确切的示例,现在没有模块。 我们也可以将代码放入类中。 由于未实例化该类,因此必须将Main(
过程声明为Shared
。 编译器将在不创建类实例的情况下调用Main()
方法。 这就是为什么必须将其声明为Shared
的原因。 Java 和 C# 的工作原理相同。
命名空间
命名空间用于在最高逻辑级别上组织代码。 他们对公开给其他程序和应用的编程元素进行分类和呈现。 在一个名称空间内,我们可以声明另一个名称空间,一个类,一个接口,一个结构,一个枚举或一个委托。
在下面的代码中,我们有两个共享相同名称空间的文件。
Option Strict On
NameSpace ZetCode
Module Example1
Public Dim x As Integer = 0
Sub Init()
x += 100
Console.WriteLine(x)
End Sub
End Module
End NameSpace
我们有一个ZetCode
命名空间。 在名称空间中,我们有一个模块Example1
。
NameSpace ZetCode
我们声明一个名为ZetCode
的命名空间。
Public Dim x As Integer = 0
在模块中,我们声明并初始化x
变量。
Sub Init()
x += 100
Console.WriteLine(x)
End Sub
我们有一个Init()
方法,其中使用了x
变量。
Option Strict On
NameSpace ZetCode
Module Example
Sub Main()
Init()
x += 100
Console.WriteLine(x)
End Sub
End Module
End NameSpace
在第二个文件中,我们使用上一个文件中的Init()
方法。
NameSpace ZetCode
我们在同一个命名空间中工作。
Init()
x += 100
Console.WriteLine(x)
我们调用Init()
过程并使用x
变量。 过程和x
变量都在不同的文件和不同的模块中定义。 但是它们是在同一个命名空间中定义的,因此我们可以使用它们。
$ ./samenamespace.exe
100
200
输出。
以下代码示例具有两个不同的名称空间。 我们使用Imports
关键字从其他名称空间导入元素。
Option Strict On
NameSpace MyMath
Public Class Basic
Public Shared PI As Double = 3.141592653589
Public Shared Function GetPi() As Double
Return Me.PI
End Function
End Class
End NameSpace
我们在MyMath
名称空间中具有Math
类的框架。 在Basic
类中,我们定义PI
常量和GetPi()
方法。
Option Strict On
Imports MyMath
NameSpace ZetCode
Public Class Example
Public Shared Sub Main()
Console.WriteLine(Basic.PI)
Console.WriteLine(Basic.GetPi())
End Sub
End Class
End NameSpace
在此文件中,我们使用MyMath
命名空间中的元素。
Imports MyMath
我们将元素从MyMath
命名空间导入到我们的命名空间中。
图:根命名空间
在 Visual Basic 2008 Express 版上,会自动创建一个根名称空间。 可以在项目属性的“应用”选项卡下找到。 删除根名称空间或将其包含在导入路径中。 例如,如果在那里有“测试”,则将行更改为Imports Testing.MyMath
。
Console.WriteLine(Basic.PI)
Console.WriteLine(Basic.GetPi())
现在我们可以使用这些元素。 在我们的例子中,PI
变量和GetPi()
方法。
Option Strict On
' Imports MyMath
NameSpace ZetCode
Public Class Example
Public Shared Sub Main()
Console.WriteLine(MyMath.Basic.PI)
Console.WriteLine(MyMath.Basic.GetPi())
End Sub
End Class
End NameSpace
请注意,我们不需要Imports
关键字。 在示例中,将其注释掉。 我们可以通过使用元素的完全限定名称来使用其他命名空间中的元素。
模块
模块用于组织代码并包装类似用途的变量,属性,事件和过程。 与类不同,模块不是类型。 可以在名称空间或文件中创建模块。 不能在另一个模块,类,结构,接口或块内创建模块。 模块中的所有成员都是隐式共享的。 模块具有“朋友”访问权限。 这意味着模块可在装配中的任何位置访问。
Option Strict On
Module First
Public x As Byte = 11
Public Sub FirstModule()
Console.WriteLine("First module")
End Sub
End Module
Module Second
Public y As Byte = 22
Public Sub SecondModule()
Console.WriteLine("Second module")
End Sub
End Module
Module Example
Sub Main()
Console.WriteLine(x)
Console.WriteLine(Second.y)
FirstModule()
SecondModule()
End Sub
End Module
我们定义了三个模块。 前两个模块具有变量和过程。 这些将在第三个模块中使用。
Module First
Public x As Byte = 11
...
End Module
我们也可以在模块内部使用访问说明符。 这样,我们可以控制模块中元素的可访问性。
Console.WriteLine(x)
Console.WriteLine(Second.y)
我们打印x
和y
变量。 它们是Public
,可以从其他模块访问。 我们可以使用模块名称来完全指定变量名称。
作用域是变量的可见性。 具有模块作用域的变量在声明了该模块的模块中可用。
Option Strict On
Module Example
Private x As Integer = 0
Sub Main()
proc1()
proc2()
proc3()
End Sub
Sub proc1()
Console.WriteLine(x)
End Sub
Sub proc2()
x += 100
Console.WriteLine(x)
End Sub
Sub proc3()
x += 100
Console.WriteLine(x)
End Sub
End Module
我们在模块内部有x
变量。 该变量在所有三个过程中均可用。
Private x As Integer = 0
这是具有模块作用域的变量。 它在任何过程外声明。
Sub proc2()
x += 100
Console.WriteLine(x)
End Sub
在proc2()
过程中,我们增加了x
变量并将其内容打印到控制台。 我们引用模块中定义的x
变量。
$ ./modulescope.exe
0
100
200
示例的输出。
过程
过程为代码项目提供了模块化。 他们应该仅执行特定任务。
Option Strict On
Module Example
Dim x As Integer = 0
Sub Main()
Console.WriteLine(x)
proc1()
proc2()
proc3()
End Sub
Sub proc1()
Dim x As Integer
x += 100
Console.WriteLine(x)
End Sub
Sub proc2()
Dim x As Integer
x += 100
Console.WriteLine(x)
End Sub
Sub proc3()
Dim x As Integer
x += 100
Console.WriteLine(x)
End Sub
End Module
在前面的代码示例中,除主过程外,我们还有三个过程。 这三个过程创建一个本地x
变量并将其打印到终端。 主要过程涉及模块x
变量。
Sub proc1()
Dim x As Integer
x += 100
Console.WriteLine(x)
End Sub
proc1()
过程创建一个本地x
变量。 此变量遮盖了,该变量在模块作用域中声明。
$ ./procedurescope.exe
0
100
100
100
主程序打印 0。其他程序打印 100 到终端。 他们创建其本地x
变量,将其初始化为 0,然后增加 100。
块作用域
重要的是要理解在If
/ End If
或While
/ End While
之类的代码块中声明的变量具有有限的块作用域和生存期。 下一个示例对此进行了说明。
Option Strict On
Module Example
Sub Main()
If True
Console.WriteLine("Inside If block")
Dim x As Integer = 0
Console.WriteLine(x)
x += 500
Console.WriteLine(x)
End If
Console.WriteLine("Outside If block")
Rem Will not compile
Rem Console.WriteLine(x)
End Sub
End Module
我们在If
/ End If
块内部声明了一个x
变量。
Rem Will not compile
Rem Console.WriteLine(x)
该变量在块外部不可用。 如果我们取消注释第二行,则该示例将无法编译。
Visual Basic 教程的这一部分专门用于组织代码。 我们提到了代码的基本组织元素,例如名称空间,模块或过程。 我们描述了与这些要素密切相关的可变作用域和持续时间。
面向对象编程
在 Visual Basic 教程的这一部分中,我们将讨论 Visual Basic 中的面向对象的编程。
那里有三种广泛使用的编程范例。 过程编程,函数式编程和面向对象的编程。 Visual Basic 支持过程式编程和面向对象的编程。
面向对象编程(OOP)是一种使用对象及其相互作用设计应用和计算机程序的编程范例。 (维基百科)
OOP 中有一些基本的编程概念:
- 抽象
- 多态
- 封装
- 继承
抽象通过建模适合该问题的类来简化复杂的现实。 多态是将运算符或函数以不同方式用于不同数据输入的过程。 封装对其他对象隐藏了类的实现细节。 继承是一种使用已经定义的类形成新类的方法。
对象
对象是 Visual Basic OOP 程序的基本构建块。 对象是数据和方法的组合。 在 OOP 程序中,我们创建对象。 这些对象通过方法进行通信。 每个对象都可以接收消息,发送消息和处理数据。
创建对象有两个步骤。 首先,我们创建一个类。 类是对象的模板。 这是一个蓝图,描述了类对象共享的状态和行为。 一个类可以用来创建许多对象。 在运行时从类创建的对象称为该特定类的实例。
Option Strict On
Module Example
Class Being
End Class
Sub Main()
Dim b as New Being
Console.WriteLine(b.ToString())
End Sub
End Module
在第一个示例中,我们创建一个简单的对象。
Class Being
End Class
这是一个简单的类定义。 模板的主体为空。 它没有任何数据或方法。
Dim b as New Being
我们创建Being
类的新实例。 为此,我们使用了New
关键字。 b
变量是创建对象的句柄。
Console.WriteLine(b.ToString())
对象的ToString()
方法给出了该对象的一些基本描述。
$ ./object.exe
Example+Being
由于类定义为空,因此我们没有太多信息。 我们获得对象类名称和模块名称,该对象的实例在此处创建。
对象属性
对象属性是捆绑在类实例中的数据。 对象属性称为实例变量或成员字段。 实例变量是在类中定义的变量,该类中的每个对象都有一个单独的副本。
Option Strict On
Module Example
Class Person
Public Name As String
End Class
Sub Main()
Dim p1 as New Person
p1.Name = "Jane"
Dim p2 as New Person
p2.Name = "Beky"
Console.WriteLine(p1.Name)
Console.WriteLine(p2.Name)
End Sub
End Module
在上面的 Visual Basic 代码中,我们有一个带有一个成员字段的Person
类。
Class Person
Public Name As String
End Class
我们声明一个Name
成员字段。 Public
关键字指定可以在Class
End Class
块之外访问成员字段。
Dim p1 as New Person
p1.Name = "Jane"
我们创建Person
类的实例。 并将Name
变量设置为"Jane"
。 我们使用点运算符来访问对象的属性。
Dim p2 as New Person
p2.Name = "Beky"
我们创建Person
类的另一个实例。 在这里,我们将变量设置为"Beky"
。
Console.WriteLine(p1.Name)
Console.WriteLine(p2.Name)
我们将变量的内容打印到控制台。
$ ./person.exe
Jane
Beky
我们看到了程序的输出。 Person
类的每个实例都有Name
成员字段的单独副本。
方法
方法是在类主体中定义的函数/过程。 它们用于通过对象的属性执行操作。 在 OOP 范式的封装概念中,方法至关重要。 例如,我们的AccessDatabase
类中可能有一个Connect
方法。 我们无需通知Connect
如何精确地连接到数据库。 我们只知道它用于连接数据库。 这对于划分编程中的职责至关重要,尤其是在大型应用中。
Option Strict On
Module Example
Class Circle
Public Radius As Integer
Public Sub SetRadius(ByVal Radius As Integer)
Me.Radius = Radius
End Sub
Public Function Area() As Double
Return Me.Radius * Me.Radius * Math.PI
End Function
End Class
Sub Main()
Dim c As New Circle
c.SetRadius(5)
Console.WriteLine(c.Area())
End Sub
End Module
在代码示例中,我们有一个Circle
类。 我们定义了两种方法。
Public Radius As Integer
我们只有一个成员字段。 它是圆的Radius
。 Public
关键字是访问说明符。 它表明该变量可以从外界完全访问。
Public Sub SetRadius(ByVal Radius As Integer)
Me.Radius = Radius
End Sub
这是SetRadius()
方法。 这是一个正常的 Visual Basic 过程。 Me
变量是一个特殊变量,我们用它来访问方法中的成员字段。
Public Function Area() As Double
Return Me.Radius * Me.Radius * Math.PI
End Function
Area()
方法返回圆的面积。 Math.PI
是内置常数。
$ ./circle.exe
78.5398163397448
运行示例。
访问修饰符
访问修饰符设置方法和成员字段的可见性。 Visual Basic 具有五个访问修饰符:Public
,Protected
,Private
,Friend
和ProtectedFriend
。 Public
成员可以从任何地方访问。 Protected
成员只能在类本身内部以及继承的和父类访问。 可以从同一程序集(exe 或 DLL)中访问Friend
成员。 ProtectedFriend
是受保护的修饰符和朋友修饰符的组合。
访问修饰符可防止意外修改数据。 它们使程序更强大。
Option Strict On
Module Example
Class Person
Public Name As String
Private Age As Byte
Public Function GetAge() As Byte
Return Me.Age
End Function
Public Sub SetAge(ByVal Age As Byte)
Me.Age = Age
End Sub
End Class
Sub Main()
Dim p as New Person
p.Name = "Jane"
p.setAge(17)
Console.WriteLine("{0} is {1} years old", _
p.Name, p.GetAge)
End Sub
End Module
在上面的程序中,我们有两个成员字段。 一个声明为Public
,另一个声明为Private
。
Public Function GetAge() As Byte
Return Me.Age
End Function
如果成员字段是Private
,则访问它的唯一方法是通过方法。 如果要在类外部修改属性,则必须将方法声明为Public
。 这是数据保护的重要方面。
Public Sub SetAge(ByVal Age As Byte)
Me.Age = Age
End Sub
SetAge()
方法使我们能够从类定义之外更改私有Age
变量。
Dim p as New Person
p.Name = "Jane"
我们创建Person
类的新实例。 因为Name
属性是Public
,所以我们可以直接访问它。 但是,不建议这样做。
p.setAge(17)
SetAge()
方法修改Age
成员字段。 由于已声明Private
,因此无法直接访问或修改。
Console.WriteLine("{0} is {1} years old", _
p.Name, p.GetAge)
最后,我们访问两个成员以构建一个字符串。
$ ./modifiers.exe
Jane is 17 years old
Running the example.
Option Strict On
Module Example
Class Base
Public Name As String = "Base"
Protected Id As Integer = 5323
Private IsDefined As Boolean = True
End Class
Class Derived
Inherits Base
Public Sub Info()
Console.WriteLine("This is Derived Class")
Console.WriteLine("Members inherited:")
Console.WriteLine(Me.Name)
Console.WriteLine(Me.Id)
'Console.WriteLine(Me.IsDefined)
End Sub
End Class
Sub Main()
Dim drv As Derived = New Derived
drv.Info()
End Sub
End Module
在前面的程序中,我们有一个Derived
类,该类继承自Base
类。 Base
类具有三个成员字段。 全部具有不同的访问修饰符。 IsDefined
成员未继承。 Private
修饰符可以防止这种情况。
Class Derived
Inherits Base
类Derived
继承自Base
类。
Console.WriteLine(Me.Name)
Console.WriteLine(Me.Id)
'Console.WriteLine(Me.IsDefined)
Public
和Protected
成员由Derived
类继承。 可以访问它们。 Private
成员未继承。 访问成员字段的行被注释。 如果我们取消注释该行,它将无法编译。
$ ./protected.exe
This is Derived Class
Members inherited:
Base
5323
运行程序,我们收到此输出。 Public
和Protected
成员是继承的,Private
成员则不是。
方法重载
方法重载允许创建多个具有相同名称的方法,它们的输入类型彼此不同。
方法重载有什么好处? Qt4 库提供了一个很好的用法示例。 QPainter
类具有三种绘制矩形的方法。 它们的名称为drawRect()
,其参数不同。 一个引用一个浮点矩形对象,另一个引用一个整数矩形对象,最后一个引用四个参数,x
,y
,width
,height
。 如果开发 Qt 的 C++ 语言没有方法重载,则库的创建者必须将其命名为drawRectRectF()
,drawRectRect()
和drawRectXYWH()
之类的方法。 方法重载的解决方案更为优雅。
Option Strict On
Module Example
Class Sum
Public Function GetSum() As Integer
Return 0
End Function
Public Function GetSum(ByVal x As Integer) As Integer
Return x
End Function
Public Function GetSum(ByVal x As Integer, _
ByVal y As Integer) As Integer
Return x + y
End Function
End Class
Sub Main()
Dim s As Sum = New Sum
Console.WriteLine(s.getSum())
Console.WriteLine(s.getSum(20))
Console.WriteLine(s.getSum(20, 30))
End Sub
End Module
我们有三种方法GetSum()
。 它们的输入参数不同。
Public Function GetSum(ByVal x As Integer) As Integer
Return x
End Function
这一个参数。
Console.WriteLine(s.getSum())
Console.WriteLine(s.getSum(20))
Console.WriteLine(s.getSum(20, 30))
我们调用这三种方法。
$ ./overloading.exe
0
20
50
这就是我们运行示例时得到的。
构造器
构造器是一种特殊的方法。 创建对象时会自动调用它。 构造器的目的是初始化对象的状态。 Visual Basic 中构造器的名称为New
。 构造器是方法,因此它们也可以重载。
Option Strict On
Module Example
Class Being
Sub New()
Console.WriteLine("Being is being created")
End Sub
Sub New(ByVal name As String)
Console.WriteLine("Being {0} is created", name)
End Sub
End Class
Sub Main()
Dim b As New Being
Dim t As New Being("Tom")
End Sub
End Module
我们有一个Being
类。 此类具有两个构造器。 第一个不带参数,第二个不带参数。
Sub New(ByVal name As String)
Console.WriteLine("Being {0} is created", name)
End Sub
该构造器采用一个String
参数。
Dim b As New Being
创建Being
类的实例。 这次,在创建对象时调用没有参数的构造器。
$ ./constructor.exe
Being is being created
Being Tom is created
这是程序的输出。
在下一个示例中,我们初始化类的数据成员。 变量的初始化是构造器的典型工作。
Option Strict On
Module Example
Class MyFriend
Private Born As Date
Private Name As String
Sub New(ByVal Name As String, ByVal Born As Date)
Me.Name = Name
Me.Born = Born
End Sub
Public Sub GetInfo()
Console.WriteLine("{0} was born on {1}", _
Me.Name, Me.Born.ToShortDateString)
End Sub
End Class
Sub Main()
Dim name As String = "Lenka"
Dim born As Date = #5/3/1990#
Dim fr As MyFriend = New MyFriend(name, born)
fr.GetInfo()
End Sub
End Module
我们有一个带有数据成员和方法的Friend
类。
Private Born As Date
Private Name As String
类定义中有两个变量。 Private
关键字是访问修饰符。 它是一种封装。 Private
关键字是限制性最强的修饰符。 它仅允许有问题的对象访问变量。 没有子孙,没有其他物件。
Sub New(ByVal Name As String, ByVal Born As Date)
Me.Name = Name
Me.Born = Born
End Sub
在构造器中,我们启动两个数据成员。 Me
变量是用于引用对象变量的处理器。
Dim fr As MyFriend = New MyFriend(name, born)
fr.GetInfo()
我们创建带有两个参数的Friend
对象。 然后,我们调用对象的GetInfo()
方法。
./constructor2.exe
Lenka was born on 5/3/1990
类常量
Visual Basic 可以创建类常量。 这些常量不属于具体对象。 他们属于类。 按照约定,常量用大写字母表示。
Option Strict On
Module Example
Class Math
Public Const PI As Double = 3.14159265359
End Class
Sub Main()
Console.WriteLine(Math.PI)
End Sub
End Module
我们有一个带有PI
常量的Math
类。
Public Const PI As Double = 3.14159265359
Const
关键字用于定义常数。
$ ./classconstant.exe
3.14159265359
运行示例。
ToString()
方法
每个对象都有一个ToString()
方法。 它返回对象的人类可读表示。 默认实现返回对象类型的标准名称。 请注意,当我们使用对象作为参数调用Console.WriteLine()
方法时,将调用ToString()
。
Option Strict On
Module Example
Class Being
Public Overrides Function ToString As String
Return "This is Being Class"
End Function
End Class
Sub Main()
Dim b as New Being
Dim o As New Object
Console.WriteLine(o.ToString())
Console.WriteLine(b.ToString())
Console.WriteLine(b)
End Sub
End Module
我们有一个Being
类,其中我们重写了ToString()
方法的默认实现。
Public Overrides Function ToString As String
Return "This is Being Class"
End Function
创建的每个类都从基Object
继承。 ToString()
方法属于此Object
类。 我们使用Overrides
关键字来通知我们正在覆盖方法。
Dim b as New Being
Dim o As New Object
我们创建两个对象。 一种自定义定义,一种内置。
Console.WriteLine(o.ToString())
Console.WriteLine(b.ToString())
我们在这两个对象上调用ToString()
方法。
Console.WriteLine(b)
正如我们之前指定的,在对象上调用Console.WriteLine()
将调用其ToString()
方法。
$ ./override.exe
System.Object
This is Being Class
This is Being Class
这是我们运行脚本时得到的。
继承
继承是使用已经定义的类形成新类的方法。 新形成的类称为派生的类,我们派生的类称为基类。 继承的重要好处是代码重用和降低程序的复杂性。 派生类(后代)将覆盖或扩展基类(祖先)的功能。
Option Strict On
Module Example
Class Being
Sub New()
Console.WriteLine("Being is created")
End Sub
End Class
Class Human
Inherits Being
Sub New()
Console.WriteLine("Human is created")
End Sub
End Class
Sub Main()
Dim h As New Human
End Sub
End Module
在此程序中,我们有两个类。 基类Being
和派生的Human
类。 派生类继承自基类。
Class Human
Inherits Being
在 Visual Basic 中,我们使用Inherits
关键字创建继承关系。
Dim h As New Human
我们实例化派生的Human
类。
$ ./inheritance.exe
Being is created
Human is created
我们可以看到两个构造器都被调用了。 首先,调用基类的构造器,然后调用派生类的构造器。
接下来是一个更复杂的示例。
Option Strict On
Module Example
Class Being
Dim Shared Count As Integer = 0
Sub New()
Count = Count + 1
Console.WriteLine("Being is created")
End Sub
Sub GetCount()
Console.WriteLine("There are {0} Beings", Count)
End Sub
End Class
Class Human
Inherits Being
Sub New()
Console.WriteLine("Human is created")
End Sub
End Class
Class Animal
Inherits Being
Sub New
Console.WriteLine("Animal is created")
End Sub
End Class
Class Dog
Inherits Animal
Sub New()
Console.WriteLine("Dog is created")
End Sub
End Class
Sub Main()
Dim h As New Human
Dim d As New Dog
d.GetCount()
End Sub
End Module
我们有四个类。 继承层次更加复杂。 Human
和Animal
类继承自Being
类。 Dog
类直接继承自Animal
类,并间接继承自Being
类。 我们还介绍了Shared
变量的概念。
Dim Shared Count As Integer = 0
我们定义一个Shared
变量。 共享成员是类的所有实例共享的成员。 在其他编程语言中,它们称为静态成员。
Sub New()
Count = Count + 1
Console.WriteLine("Being is created")
End Sub
每次实例化Being
类时,我们都会将Count
变量加 1。 这样,我们就可以跟踪创建的实例数。
Class Animal
Inherits Being
...
Class Dog
Inherits Animal
...
Animal
继承自Being
,Dog
继承自动物。 Dog
也间接继承自Being
。
Dim h As New Human
Dim d As New Dog
d.GetCount
我们从Human
和Dog
类创建实例。 我们称为Dog
对象的GetCount()
方法。
$ ./inheritance2.exe
Being is created
Human is created
Being is created
Animal is created
Dog is created
There are 2 Beings
Human
对象调用两个构造器:Dog
对象调用三个构造器。 有两个实例化的存在。
抽象类和方法
抽象类无法实例化。 如果一个类至少包含一个抽象方法,则也必须将其声明为抽象方法。 抽象方法无法实现,它们仅声明方法的签名。 当我们从抽象类继承时,所有抽象方法都必须由派生类实现。 此外,必须以较少受限制的可见性声明这些方法。
与接口不同,抽象类可能具有完全实现的方法,并且可能具有定义的成员字段。 因此,抽象类可以提供部分实现。 程序员经常将一些通用功能放入抽象类中。 这些抽象类随后会被子类化以提供更具体的实现。 例如,Qt 图形库具有QAbstractButton
,它是按钮小部件的抽象基类,提供按钮所共有的功能。 按钮Q3Button
,QCheckBox
,QPushButton
,QRadioButton
和QToolButton
都从此基本抽象类继承。
正式地说,抽象类用于强制执行协议。 协议是一组操作,所有实现对象都必须支持。
Option Strict On
Module Example
MustInherit Class Drawing
Protected x As Integer = 0
Protected y As Integer = 0
Public MustOverride Function Area() As Double
Public Function GetCoordinates() As String
Return String.Format("x: {0}, y: {1}", _
Me.x, Me.y)
End Function
End Class
Class Circle
Inherits Drawing
Private Radius As Integer
Sub New(ByVal x As Integer, ByVal y As Integer, _
ByVal r As Integer)
Me.x = x
Me.y = y
Me.Radius = r
End Sub
Public Overrides Function Area() As Double
Return Me.Radius * Me.Radius * Math.PI
End Function
Public Overrides Function ToString() As String
Return String.Format("Circle, at x: {0}, y: {1}, radius: {2}", _
Me.x, Me.y, Me.Radius)
End Function
End Class
Sub Main()
Dim c as New Circle(12, 45, 22)
Console.WriteLine(c)
Console.WriteLine("Area of circle: {0}", c.Area())
Console.WriteLine(c.GetCoordinates())
End Sub
End Module
我们有一个抽象基类Drawing
。 该类定义两个成员字段,定义一个方法并声明一个方法。 一种方法是抽象的,另一种是完全实现的。 Drawing
类是抽象的,因为我们无法绘制它。 我们可以画一个圆,一个点或一个正方形。 Drawing
类对我们可以绘制的对象具有一些通用功能。
MustInherit Class Drawing
在 Visual Basic 中,我们使用MustInherit
关键字定义抽象类。
Public MustOverride Function Area() As Double
抽象方法前面带有MustOverride
关键字。
Class Circle
Inherits Drawing
圆是Drawing
类的子类。 它必须实现抽象Area()
方法。
$ ./abstractclass.exe
Circle, at x: 12, y: 45, radius: 22
Area of circle: 1520.53084433746
x: 12, y: 45
程序的输出。
这是 Visual Basic 中 OOP 描述的第一部分。
Visual Basic 中的面向对象编程 II
在 Visual Basic 教程的这一章中,我们将继续以 Visual Basic 语言描述 OOP。
接口
遥控器是观众和电视之间的接口。 它是此电子设备的接口。 外交礼仪指导外交领域的所有活动。 道路规则是驾车者,骑自行车者和行人必须遵守的规则。 编程中的接口类似于前面的示例。
接口是:
- API
- 合约
对象通过这些方法与外界交互。 实际的实现对程序员而言并不重要,或者也可能是秘密的。 公司可能会出售图书馆,但它不想透露实际的实现情况。 程序员可能会在 GUI 工具箱的窗口中调用Maximize
方法,但对如何实现此方法一无所知。 从这个角度来看,接口是一种方法,通过这些方法,对象可以与外界交互,而不会过多地暴露其内部工作原理。
从第二个角度来看,接口就是契约。 如果达成协议,则必须遵循。 它们用于设计应用的架构。 他们帮助组织代码。
接口是完全抽象的类型。 它们使用Interface
关键字声明。 接口只能具有方法签名和常量。 接口中声明的所有方法签名必须是公共的。 他们不能具有完全实现的方法,也不能具有成员字段。 Visual Basic 类可以实现任何数量的接口。 一个接口还可以扩展任何数量的接口。 实现接口的类必须实现接口的所有方法签名。
接口用于模拟多重继承。 Visual Basic 类只能从一个类继承。 Visual Basic 类可以实现多个接口。 使用接口的多重继承与继承方法和变量无关。 它是关于继承想法或合同的,这些想法或合同由接口描述。
接口和抽象类之间有一个重要的区别。 抽象类为继承层次结构中相关的类提供部分实现。 另一方面,可以通过彼此不相关的类来实现接口。 例如,我们有两个按钮。 经典按钮和圆形按钮。 两者都继承自抽象按钮类,该类为所有按钮提供了一些通用功能。 实现类是相关的,因为它们都是按钮。 另一个示例可能具有类Database
和SignIn
。 它们彼此无关。 我们可以应用ILoggable
接口,该接口将迫使他们创建执行日志记录的方法。
Option Strict On
Module Example
Interface IInfo
Sub DoInform()
End Interface
Class Some
Implements IInfo
Sub DoInform() Implements IInfo.DoInform
Console.WriteLine("This is Some Class")
End Sub
End Class
Sub Main()
Dim sm As New Some
sm.DoInform()
End Sub
End Module
这是一个演示接口的简单 Visual Basic 程序。
Interface IInfo
Sub DoInform()
End Interface
这是接口IInfo
。 它具有DoInform()
方法签名。
Class Some
Implements IInfo
我们使用Implements
从接口实现。
Sub DoInform() Implements IInfo.DoInform
Console.WriteLine("This is Some Class")
End Sub
该类提供了DoInform()
方法的实现。 Implements
关键字明确指定了我们正在实现的方法签名。
下一个示例显示了一个类如何实现多个接口。
Option Strict On
Module Example
Interface Device
Sub SwitchOn()
Sub SwitchOff()
End Interface
Interface Volume
Sub VolumeUp()
Sub VolumeDown()
End Interface
Interface Pluggable
Sub PlugIn()
Sub PlugOff()
End Interface
Class CellPhone
Implements Device, Volume, Pluggable
Public Sub SwitchOn() Implements Device.SwitchOn
Console.WriteLine("Switching on")
End Sub
Public Sub SwitchOff() Implements Device.SwitchOff
Console.WriteLine("Switching on")
End Sub
Public Sub VolumeUp() Implements Volume.VolumeUp
Console.WriteLine("Volume up")
End Sub
Public Sub VolumeDown() Implements Volume.VolumeDown
Console.WriteLine("Volume down")
End Sub
Public Sub PlugIn() Implements Pluggable.PlugIn
Console.WriteLine("Plugging In")
End Sub
Public Sub PlugOff() Implements Pluggable.PlugOff
Console.WriteLine("Plugging Off")
End Sub
End Class
Sub Main()
Dim o As New CellPhone
o.SwitchOn()
o.VolumeUp()
o.PlugIn()
End Sub
End Module
我们有一个CellPhone
类,它从三个接口继承。
Class CellPhone
Implements Device, Volume, Pluggable
该类实现所有三个接口,并用逗号分隔。 CellPhone
类必须实现来自所有三个接口的所有方法签名。
$ ./interface.exe
Switching on
Volume up
Plugging In
运行程序。
下一个示例显示接口如何从多个其他接口继承。
Option Strict On
Module Example
Interface IInfo
Sub DoInform()
End Interface
Interface IVersion
Sub GetVersion()
End Interface
Interface ILog
Inherits IInfo, IVersion
Sub DoLog
End Interface
Class DBConnect
Implements ILog
Public Sub DoInform() Implements IInfo.DoInform
Console.WriteLine("This is DBConnect class")
End Sub
Public Sub GetVersion() Implements IVersion.GetVersion
Console.WriteLine("Version 1.02")
End Sub
Public Sub DoLog() Implements ILog.DoLog
Console.WriteLine("Logging")
End Sub
Public Sub Connect()
Console.WriteLine("Connecting to the database")
End Sub
End Class
Sub Main()
Dim db As New DBConnect
db.DoInform()
db.GetVersion()
db.DoLog()
db.Connect()
End Sub
End Module
我们定义了三个接口。 我们可以按层次组织接口。
Interface ILog
Inherits IInfo, IVersion
ILog
接口继承自其他两个接口。
Public Sub DoInform() Implements IInfo.DoInform
Console.WriteLine("This is DBConnect class")
End Sub
DBConnect
类实现DoInform()
方法。 该方法由该类实现的ILog
接口继承。
$ ./interface2.exe
This is DBConnect class
Version 1.02
Logging
Connecting to the database
输出。
多态
多态是以不同方式将运算符或函数用于不同数据输入的过程。 实际上,多态意味着如果类B
从类A
继承,则不必继承关于类A
的所有内容; 它可以完成A
类所做的某些事情。 (维基百科)
通常,多态是以不同形式出现的能力。 从技术上讲,它是重新定义派生类的方法的能力。 多态与将特定实现应用于接口或更通用的基类有关。
多态是重新定义派生类的方法的能力。
Option Strict On
Module Example
MustInherit Class Shape
Protected x As Integer
Protected y As Integer
Public MustOverride Function Area() As Integer
End Class
Class Rectangle
Inherits Shape
Sub New(ByVal x As Integer, ByVal y As Integer)
Me.x = x
Me.y = y
End Sub
Public Overrides Function Area() As Integer
Return Me.x * Me.y
End Function
End Class
Class Square
Inherits Shape
Sub New(ByVal x As Integer)
Me.x = x
End Sub
Public Overrides Function Area() As Integer
Return Me.x * Me.x
End Function
End Class
Sub Main()
Dim shapes() As Shape = { New Square(5), _
New Rectangle(9, 4), New Square(12) }
For Each shape As Shape In shapes
Console.WriteLine(shape.Area())
Next
End Sub
End Module
在上面的程序中,我们有一个抽象的Shape
类。 该类变形为两个后代类,即Rectangle
和Square
。 两者都提供了自己的Area()
方法的实现。 多态为 OOP 系统带来了灵活性和可伸缩性。
Public Overrides Function Area() As Integer
Return Me.x * Me.y
End Function
...
Public Overrides Function Area() As Integer
Return Me.x * Me.x
End Function
Rectangle
和Square
类具有Area
方法的自己的实现。
Dim shapes() As Shape = { New Square(5), _
New Rectangle(9, 4), New Square(12) }
我们创建三个形状的数组。
For Each shape As Shape In shapes
Console.WriteLine(shape.Area())
Next
我们遍历每个形状并在其上调用Area
方法。 编译器为每种形状调用正确的方法。 这就是多态的本质。
NotOverridable
,NotInheritable
NotOverridable
方法不能被覆盖,NotInheritable
类不能从中继承。 这些关键字与应用设计有关。 我们不应从某些类继承,并且不应重写某些方法。
Option Strict On
Module Example
Class Base
Public NotOverridable Sub Say()
Console.WriteLine("Base class")
End Sub
End Class
Class Derived
Inherits Base
Public Overrides Sub Say()
Console.WriteLine("Derived class")
End Sub
End Class
Sub Main()
Dim o As Base = New Derived
o.Say()
End Sub
End Module
该程序将无法编译。 我们收到错误消息"Public Overrides Sub Say()"
无法覆盖"Public NotOverridable Sub Say()"
,因为它被声明为"NotOverridable"
。
Option Strict On
Module Example
NotInheritable Class Math
Public Shared Function getPI() As Single
Return 3.141592
End Function
End Class
Class DerivedMath
Inherits Math
Public Sub Say()
Console.WriteLine("DerivedMath class")
End Sub
End Class
Sub Main()
Dim o As DerivedMath = New DerivedMath
o.Say()
End Sub
End Module
在上面的程序中,我们有一个原型基础Math
类。 该类的唯一目的是为程序员提供一些有用的方法和常量。 (出于简单起见,在我们的案例中,我们只有一种方法。)它不是从继承而创建的。 为了防止不知情的其他程序员从此类中派生,创建者创建了NotInheritable
类。 如果尝试编译该程序,则会出现以下错误:'DerivedMath'无法从类'Math'继承,因为'Math'被声明为'NotInheritable'。
深拷贝与浅拷贝
数据复制是编程中的重要任务。 对象是 OOP 中的复合数据类型。 对象中的成员字段可以按值或按引用存储。 可以以两种方式执行复制。
浅表副本将所有值和引用复制到新实例中。 引用所指向的数据不会被复制; 仅指针被复制。 新的引用指向原始对象。 对引用成员的任何更改都会影响两个对象。
深层副本将所有值复制到新实例中。 如果成员存储为引用,则深层副本将对正在引用的数据执行深层副本。 创建一个引用对象的新副本。 并存储指向新创建对象的指针。 对这些引用对象的任何更改都不会影响该对象的其他副本。 深拷贝是完全复制的对象。
如果成员字段是值类型,则将对该字段进行逐位复制。 如果该字段是引用类型,则复制引用,但不是复制引用的对象。 因此,原始对象中的引用和克隆对象中的引用指向同一对象。 (来自 programmingcorner.blogspot.com 的明确解释)
接下来的两个示例将对对象执行浅表复制和深表复制。
Option Strict On
Module Example
Class Color
Public red as Byte
Public green as Byte
Public blue as Byte
Sub New(red As Byte, green As Byte, _
blue As Byte)
Me.red = red
Me.green = green
Me.blue = blue
End Sub
End Class
Class MyObject
Implements ICloneable
Public Id As Integer
Public Size As String
Public Col As Color
Sub New(Id As Integer, Size As String, _
Col As Color)
Me.Id = Id
Me.Size = Size
Me.Col = Col
End Sub
Public Function Clone() As Object _
Implements ICloneable.Clone
Return New MyObject(Me.Id, Me.Size, Me.Col)
End Function
Public Overrides Function ToString() As String
Dim s As String
s = String.Format("Id: {0}, Size: {1}, Color:({2}, {3}, {4})", _
Me.Id, Me.Size, Me.Col.red, Me.Col.green, Me.Col.blue)
Return s
End Function
End Class
Sub Main()
Dim col As New Color(23, 42, 223)
Dim obj1 As New MyObject(23, "small", col)
Dim obj2 As MyObject
obj2 = CType(obj1.Clone(), MyObject)
obj2.Id += 1
obj2.Size = "big"
obj2.Col.red = 255
Console.WriteLine(obj1)
Console.WriteLine(obj2)
End Sub
End Module
这是一个浅表副本的示例。 我们定义了两个自定义对象:MyObject
和Color
。 MyObject
对象将具有对Color
对象的引用。
Class MyObject
Implements ICloneable
我们应该为要克隆的对象实现ICloneable
接口。
Public Function Clone() As Object _
Implements ICloneable.Clone
Return New MyObject(Me.Id, Me.Size, Me.Col)
End Function
ICloneable
接口迫使我们创建Clone()
方法。 此方法返回具有复制值的新对象。
Dim col As New Color(23, 42, 223)
我们创建Color
对象的实例。
Dim obj1 As New MyObject(23, "small", col)
创建MyObject
对象的实例。 它将Color
对象的实例传递给其构造器。
obj2 = CType(obj1.Clone(), MyObject)
我们创建obj1
对象的浅表副本,并将其分配给obj2
变量。 Clone()
方法返回Object
,我们期望MyObject
。 这就是我们进行显式转换的原因。
obj2.Id += 1
obj2.Size = "big"
obj2.Col.red = 255
在这里,我们修改复制对象的成员字段。 我们增加Id
,将Size
更改为"big"
,然后更改颜色对象的红色部分。
Console.WriteLine(obj1)
Console.WriteLine(obj2)
Console.WriteLine()
方法调用obj2
对象的ToString()
方法,该方法返回对象的字符串表示形式。
Id: 23, Size: small, Color:(255, 42, 223)
Id: 24, Size: big, Color:(255, 42, 223)
我们可以看到 ID 分别为 23 和 24。大小不同。 small
和big
。 但是,这两个实例的颜色对象的红色部分相同:255。更改克隆对象的成员值(Id
,Size
)不会影响原始对象。 更改引用对象(Col
)的成员也影响了原始对象。 换句话说,两个对象都引用内存中的同一颜色对象。
要更改此行为,我们接下来将做一个深层复制。
Option Strict On
Module Example
Class Color
Implements ICloneable
Public Red as Byte
Public Green as Byte
Public Blue as Byte
Sub New(Red As Byte, Green As Byte, _
Blue As Byte)
Me.Red = Red
Me.Green = Green
Me.Blue = Blue
End Sub
Public Function Clone() As Object _
Implements ICloneable.Clone
Return New Color(Me.Red, Me.Green, Me.Blue)
End Function
End Class
Class MyObject
Implements ICloneable
Public Id As Integer
Public Size As String
Public Col As Color
Sub New(Id As Integer, Size As String, _
Col As Color)
Me.Id = Id
Me.Size = Size
Me.Col = Col
End Sub
Public Function Clone() As Object _
Implements ICloneable.Clone
Return New MyObject(Me.Id, Me.Size, CType(Me.Col.Clone(), Color))
End Function
Public Overrides Function ToString() As String
Dim s As String
s = String.Format("Id: {0}, Size: {1}, Color:({2}, {3}, {4})", _
Me.Id, Me.Size, Me.Col.Red, Me.Col.Green, Me.Col.Blue)
Return s
End Function
End Class
Sub Main()
Dim col As New Color(23, 42, 223)
Dim obj1 As New MyObject(23, "small", col)
Dim obj2 As MyObject
obj2 = CType(obj1.Clone(), MyObject)
obj2.Id += 1
obj2.Size = "big"
obj2.Col.Red = 255
Console.WriteLine(obj1)
Console.WriteLine(obj2)
End Sub
End Module
在此程序中,我们对对象执行深层复制。
Class Color
Implements ICloneable
现在Color
类实现了ICloneable
接口。
Public Function Clone() As Object _
Implements ICloneable.Clone
Return New Color(Me.Red, Me.Green, Me.Blue)
End Function
我们也为Color
类提供了Clone()
方法。 这有助于创建引用对象的副本。
Public Function Clone() As Object _
Implements ICloneable.Clone
Return New MyObject(Me.Id, Me.Size, CType(Me.Col.Clone(), Color))
End Function
现在,当我们克隆MyObject
时,我们以Col
引用类型调用Clone()
方法。 这样,我们也可以获得颜色值的副本。
$ ./deepcopy.exe
Id: 23, Size: small, Color:(23, 42, 223)
Id: 24, Size: big, Color:(255, 42, 223)
现在,引用的Color
对象的红色部分不相同。 原始对象保留了其先前的 23 值。
异常
异常是为处理异常的发生而设计的,这些特殊情况会改变程序执行的正常流程。 引发或引发异常。
在执行应用期间,许多事情可能出错。 磁盘可能已满,我们无法保存文件。 互联网连接可能断开,我们的应用尝试连接到站点。 所有这些都可能导致我们的应用崩溃。 为避免发生这种情况,我们必须应对可能发生的所有可能的错误。 为此,我们可以使用异常处理。
Try
,Catch
和Finally
关键字用于处理异常。
Option Strict On
Module Example
Sub Main()
Dim x As Integer = 100
Dim y As Integer = 0
Dim z As Double
Try
z = x \ y
Catch e As Exception
Console.WriteLine(e.Message)
End Try
End Sub
End Module
在上面的程序中,我们有意将数字除以零。 这会导致错误。
Try
z = x \ y
...
End Try
容易出错的语句放在Try
关键字之后。
Catch e As Exception
Console.WriteLine(e.Message)
...
异常类型跟随Catch
关键字。 在我们的例子中,我们有一个通用的Exception
,它将捕获任何类型的异常。 有一些通用的异常,还有一些更具体的异常。 发生错误时,将执行Catch
关键字后面的语句。 发生异常时,将创建一个异常对象。 从该对象中,我们获得Message
属性并将其打印到控制台。
当前上下文中任何未捕获的异常都会传播到更高的上下文,并寻找适当的 catch
块来处理它。 如果找不到任何合适的catch
块,则 .NET 运行时的默认机制将终止整个程序的执行。
Option Strict On
Module Example
Sub Main()
Dim z As Double
Dim x As Integer = 100
Dim y As Integer = 0
z = x \ y
End Sub
End Module
在此程序中,我们除以零。 我们没有自定义异常处理。 在 Visual Basic 2008 Express 上,我们收到以下错误消息:“未处理的异常:System.DivideByZeroException
:试图除以零。”。
Option Strict On
Imports System.IO
Module Example
Dim fs As FileStream
Sub Main()
Try
fs = File.Open("file", FileMode.OpenOrCreate)
Console.WriteLine(fs.Length)
Catch e As IOException
Console.WriteLine("IO Error")
Console.WriteLine(e.Message)
Finally
Console.WriteLine("Finally")
If fs.CanRead = True Then
fs.Close()
End If
End Try
End Sub
End Module
始终执行Finally
关键字之后的语句。 它通常用于清理任务,例如关闭文件或清除缓冲区。
Catch e As IOException
Console.WriteLine("IO Error")
Console.WriteLine(e.Message)
在这种情况下,我们捕获了特定的IOException
异常。
Finally
Console.WriteLine("Finally")
If fs.CanRead = True Then
fs.Close()
End If
这些行确保关闭文件处理器。
Option Strict On
Module Example
Sub Main()
Dim x As Integer
Dim y As Integer
Dim z As Double
Try
Console.Write("Enter first number: ")
x = Convert.ToInt32(Console.ReadLine())
Console.Write("Enter second number: ")
y = Convert.ToInt32(Console.ReadLine())
z = x / y
Console.WriteLine("Result: {0:D} / {1:D} = {2:D}", x, y, z)
Catch e As DivideByZeroException
Console.WriteLine("Cannot divide by zero.")
Catch e As FormatException
Console.WriteLine("Wrong format of number.")
Catch e As Exception
Console.WriteLine(e.Message)
End Try
End Sub
End Module
在此示例中,我们捕获了各种异常。 请注意,更具体的异常应先于一般的异常。 我们从控制台读取两个数字,并检查零除错误和数字格式错误。
$ ./passing.exe
Enter first number: et
Wrong format of number.
运行示例。
Option Strict On
Module Example
Class BigValueException
Inherits Exception
Sub New(ByVal msg As String)
MyBase.New(msg)
End Sub
End Class
Sub Main()
Dim x As Integer = 340004
Const LIMIT As Integer = 333
Try
If (x > LIMIT) Then
Throw New BigValueException("Exceeded the maximum value")
End If
Catch e As BigValueException
Console.WriteLine(e.Message)
End Try
End Sub
End Module
假设我们处于无法处理大量数字的情况。
Class BigValueException
Inherits Exception
我们有一个BigValueException
类。 该类派生自内置的Exception
类。
Dim Const LIMIT As Integer = 333
大于此常数的数字在我们的程序中被视为big
。
Sub New(ByVal msg As String)
MyBase.New(msg)
End Sub
在构造器内部,我们称为父级的构造器。 我们将消息传递给父级。
If (x > LIMIT) Then
Throw New BigValueException("Exceeded the maximum value")
End If
如果该值大于限制,则抛出自定义异常。 我们给异常消息"Exceeded the maximum value"
。
Catch e As BigValueException
Console.WriteLine(e.Message)
我们捕获到异常并将其消息打印到控制台。
属性
属性是特殊的类成员。 我们使用预定义的设置和获取方法来访问和修改它们。 属性读取和写入会转换为获取和设置方法调用。 与使用自定义方法调用(例如object.GetName()
)相比,使用字段符号(例如object.Name
)访问变量更容易。 但是,就属性而言,我们仍然具有封装和信息隐藏的优势。
Option Strict On
Module Example
Class Person
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set (Byval Value As String)
_name = Value
End Set
End Property
End Class
Sub Main()
Dim p as New Person
p.Name = "Jane"
Console.WriteLine(p.Name())
End Sub
End Module
我们有一个带有一个属性的简单Person
类。
Public Property Name() As String
...
End Property
我们使用Property
关键字在 Visual Basic 中创建属性。
Get
Return _name
End Get
我们使用预定义的Get
关键字为_name
字段创建访问器方法。
Set (Byval Value As String)
_name = Value
End Set
类似地,Set
关键字为_name
字段创建一个修改器方法。
Dim p as New Person
p.Name = "Jane"
Console.WriteLine(p.Name())
我们创建Person
类的实例。 我们使用字段符号访问成员字段。
$ ./properties.exe
Jane
这是该计划的结果。
委托
委托是 .NET Framework 使用的一种类型安全的函数指针。 委托通常用于实现回调和事件监听器。
Option Strict On
Module Example
Public Delegate Sub NameDelegate(ByVal msg As String)
Class Person
Private FirstName As String
Private SecondName As String
Sub New(First As String, Second As String)
Me.FirstName = First
Me.SecondName = Second
End Sub
Public Sub ShowFirstName(msg As String)
Console.WriteLine(msg & Me.FirstName)
End Sub
Public Sub ShowSecondName(msg As String)
Console.WriteLine(msg & Me.SecondName)
End Sub
End Class
Sub Main()
Dim nDelegate As NameDelegate
Dim per As New Person("Fabius", "Maximus")
nDelegate = AddressOf per.ShowFirstName
nDelegate("Call 1: ")
nDelegate = AddressOf per.ShowSecondName
nDelegate("Call 2: ")
End Sub
End Module
在此示例中,我们只有一名委托。 该委托用于指向Person
类的两个方法。 方法与委托一起调用。
Public Delegate Sub NameDelegate(ByVal msg As String)
使用Delegate
关键字创建委托。 委托签名必须与委托调用的方法的签名匹配。
Dim nDelegate As NameDelegate
在这里,我们创建一个自定义委托类型的变量。
nDelegate = AddressOf per.ShowFirstName
nDelegate("Call 1: ")
AddressOf
运算符用于获取对ShowFirstName(
方法的引用。 现在我们指向该方法,我们可以通过委托来调用它。
$ ./simpledelegate.exe
Call 1: Fabius
Call 2: Maximus
这两个名称都是通过委托打印的。
事件
事件是由某些操作触发的消息。 点击按钮或时钟的滴答声就是这样的动作。 触发事件的对象称为发送者,而接收事件的对象称为接收者。
Option Strict On
Module Example
Public Event ValueFive()
Dim Random As Integer
Public Sub Main()
AddHandler ValueFive, AddressOf OnFiveEvent
For i As Integer = 0 To 10
Randomize()
Random = CInt(Rnd() * 7)
Console.WriteLine(Random)
If Random = 5 Then
RaiseEvent ValueFive()
End If
Next
End Sub
Public Sub OnFiveEvent()
Console.WriteLine("Five Event occured")
End Sub
End Module
我们有一个简单的示例,可以在其中创建和启动事件。 生成一个随机数。 如果数字等于 5,则会生成FiveEvent
事件。
Public Event ValueFive()
使用Event
关键字声明事件。
AddHandler ValueFive, AddressOf OnFiveEvent
在这里,我们将名为ValueFive()
的事件插入OnFiveEvent()
子例程。 换句话说,如果触发了ValueFive
事件,则将执行OnFiveEvent()
子例程。
If Random = 5 Then
RaiseEvent ValueFive()
End If
当随机数等于 5 时,我们引发ValueFive
事件。 我们使用RaiseEvent
关键字。
$ ./event.exe
0
1
5
Five Event occured
2
5
Five Event occured
6
7
6
3
3
1
该程序的结果可能如下所示。
接下来,我们有一个更复杂的示例。
Option Strict On
Namespace EventSample
Public Class FiveEventArgs
Inherits EventArgs
Public Count As Integer
Public Time As Date
Public Sub New(ByVal Count As Integer, ByVal Time As Date)
Me.Count = Count
Me.Time = Time
End Sub
End Class
Public Class Five
Private Count As Integer = 0
Public Sub OnFiveEvent(ByVal source As Object, _
ByVal e As FiveEventArgs)
Console.WriteLine("Five event {0} occured at {1}", _
e.Count, e.Time)
End Sub
End Class
Public Class RandomGenerator
Public Event ValueFive(ByVal source As Object, _
ByVal e As FiveEventArgs)
Public Sub Generate()
Dim Count As Integer = 0
Dim args As FiveEventArgs
For i As Byte = 0 To 10
Dim Random As Integer
Randomize()
Random = CInt(Rnd * 6)
Console.WriteLine(Random)
If Random = 5 Then
Count += 1
args = New FiveEventArgs(Count, Now)
RaiseEvent ValueFive(Me, args)
End If
Next
End Sub
End Class
Public Class Example
Public Shared Sub Main()
Dim five As New Five
Dim gen As New RandomGenerator
AddHandler gen.ValueFive, AddressOf five.OnFiveEvent
gen.Generate()
End Sub
End Class
End Namespace
我们有四个类。 FiveEventArgs
随事件对象一起传送一些数据。 Five
类封装了事件对象。 RandomGenerator
类负责生成随机数。 它是事件发送者。 最后是Example
类,它是主要的应用对象,具有Main()
方法。
Public Class FiveEventArgs
Inherits EventArgs
Public Count As Integer
Public Time As Date
...
FiveEventArgs
在事件对象内部传送数据。 它继承自EventArgs
基类。 Count
和Time
成员是将被初始化并随事件携带的数据。
If Random = 5 Then
Count += 1
args = New FiveEventArgs(Count, Now)
RaiseEvent ValueFive(Me, args)
End If
如果生成的随机数等于 5,我们用当前的Count
和Date
值实例化FiveEventArgs
类。 Count
变量对生成此事件的次数进行计数。 Time
值保留事件生成的时间。 使用带有发送者对象和事件参数的RaiseEvent
关键字发送事件。
AddHandler gen.ValueFive, AddressOf five.OnFiveEvent
我们将ValueFive
事件插入其处理器。
$ ./event2.exe
3
6
5
Five event 1 occured at 9/15/2010 5:06:13 PM
3
5
Five event 2 occured at 9/15/2010 5:06:13 PM
6
3
2
5
Five event 3 occured at 9/15/2010 5:06:13 PM
2
5
Five event 4 occured at 9/15/2010 5:06:13 PM
这是我在计算机上得到的输出。
在 Visual Basic 教程的这一部分中,我们继续讨论 Visual Basic 中的面向对象编程。