ZetCode--NET-教程-二-

ZetCode .NET 教程(二)

原文:ZetCode

协议:CC BY-NC-SA 4.0

基本概念

原文: https://zetcode.com/lang/visualbasic/basics/

在 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语句可以为OnOff。 默认值为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 中,选择项目属性。 在“调试”选项卡中,有一个文本区域用于指定命令行参数。

Command line arguments

图:命令行参数

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 数据类型

原文: https://zetcode.com/lang/visualbasic/datatypes/

在 Visual Basic 教程的这一部分中,我们将讨论数据类型。

计算机程序可以处理数据。 用于各种数据类型的工具是现代计算机语言的基本组成部分。 根据维基百科的定义,data type是一组值,以及对这些值的允许操作。

Visual Basic 中的两种基本数据类型是值类型和引用类型。 基本类型(字符串除外),枚举和结构是值类型。 类,字符串,标准模块,接口,数组和委托是引用类型。 每种类型都有一个默认值。 引用类型在堆上创建。 引用类型的生存期由 .NET 框架管理。 引用类型的默认值为空引用。 分配给引用类型的变量会创建引用的副本,而不是引用值的副本。 值类型在栈上创建。 生存期由变量的生存期决定。 分配给值类型的变量会创建要分配的值的副本。 值类型具有不同的默认值。 例如,布尔默认值为False,十进制为 0,字符串为空字符串""

布尔值

我们的世界建立了双重性。 有天与地,水与火,阴与阳,男人与女人,爱与恨。 在 Visual Basic 中,Boolean数据类型是具有以下两个值之一的原始数据类型:TrueFalse。 这是基本的数据类型。 在计算机程序中非常常见。

快乐的父母正在等待孩子的出生。 他们为两种可能性都选择了名称。 如果要成为男孩,他们选择了约翰。 如果要成为女孩,他们会选择杰西卡。

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变量设置为TrueFalse

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 中,我们有三种重要的浮点类型:SingleDoubleDecimal

VB 别名 .NET 类型 大小 精度 范围
Single System.Single 4 字节 7 位数 1.5 x 10^-453.4 x 10^38
Double System.Double 8 字节 15-16 位数 5.0 x 10^-3241.7 x 10^308
Decimal System.Decimal 16 字节 28-29 位小数 1.0 x 10^-287.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

SingleDouble值以不同的精度存储。 比较浮点值时应格外小心。

$ 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 中的字符串

原文: https://zetcode.com/lang/visualbasic/strings/

在 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 教程的这一部分介绍了字符串。

运算符

原文: https://zetcode.com/lang/visualbasic/operators/

在 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)

关系运算符始终导致布尔值。 这两行显示FalseTrue

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 FalseFalse 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

AndAlsoOrElse运算符经过短路求值。 短路求值意味着仅当第一个参数不足以确定表达式的值时才求值第二个参数:当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)

在第一种情况下,我们得到Falseo1o2是两个不同的对象。 在第二种情况下,我们得到Trueo3o2指的是同一对象。

按位运算符

小数对人类是自然的。 二进制数是计算机固有的。 二进制,八进制,十进制或十六进制符号仅是相同数字的符号。 按位运算符使用二进制数的位。 像 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运算符组合FalseTrue,最后得到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 教程的这一部分中,我们介绍了运算符。

控制流

原文: https://zetcode.com/lang/visualbasic/flowcontrol/

在 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 > 0true,因此将执行块内的语句。

$ ./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

可以在由IfEnd 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语句是选择控制流语句。 它允许变量或表达式的值通过多路分支控制程序执行的流程。 与使用IfElse 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

从控制台读取一个值。 我们只能使用数值数据。 TryCatchEnd 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关键字在WhileEnd 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循环的不正确处理可能会导致循环不断。

可以至少运行一次该语句。 即使不满足条件。 为此,我们可以使用DoLoop 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 程序将给出此输出。

ExitContinue语句

Exit语句可用于终止由WhileForSelect语句定义的块。

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语句用于跳过循环的一部分,并继续循环的下一个迭代。 它可以与DoForWhile语句结合使用。

在下面的示例中,我们将打印一个数字列表,这些数字不能除以 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 数组

原文: https://zetcode.com/lang/visualbasic/arrays/

在 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 中的过程&函数

原文: https://zetcode.com/lang/visualbasic/procedures/

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

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

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

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

过程

过程是SubEnd 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。将这些数字相加并将结果打印到控制台。

函数

函数是FunctionEnd 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 支持将参数传递给函数的两种方法。 按值和引用。 为此,我们有两个关键字。 ByValByRef。 当我们按值传递参数时,该函数仅适用于值的副本。 当我们处理大量数据时,这可能会导致性能开销。

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

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

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

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()过程在ab变量之间交换数字。 原始变量不受影响。

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

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

Swap(a, b)

我们称为Swap()过程。 该过程将ab变量作为参数。

temp = a
a = b
b = temp

Swap()过程中,我们更改了值。 请注意,ab变量是在本地定义的。 它们仅在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

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

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 中组织代码

原文: https://zetcode.com/lang/visualbasic/organizingcode/

在 Visual Basic 教程的这一部分中,我们将展示如何组织代码。 我们将介绍模块,过程和名称空间以及作用域。

Visual Basic 语句被组织为块,模块,类和名称空间。 这有助于使代码更具可维护性和鲁棒性。 正确的代码组织可以防止在代码中出错。

Visual Basic 程序的基本构建块是:

  • 程序集
  • 命名空间
  • 模块
  • 过程和函数
  • 语句

程序集是 DLL 或 exe 文件。 程序集是用于部署,版本控制和安全性的已编译代码库。 名称空间是提供项目上下文的抽象容器。 模块是在其整个命名空间中可用的引用类型。 类是 OOP 程序的基本构建块。 过程是为执行特定任务而创建的程序的一个单元。 块是由IfWhile之类的某些关键字提供的 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命名空间导入到我们的命名空间中。

Root namespace

图:根命名空间

在 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)

我们打印xy变量。 它们是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 IfWhile / 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 教程的这一部分专门用于组织代码。 我们提到了代码的基本组织元素,例如名称空间,模块或过程。 我们描述了与这些要素密切相关的可变作用域和持续时间。

面向对象编程

原文: https://zetcode.com/lang/visualbasic/oopi/

在 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

我们只有一个成员字段。 它是圆的RadiusPublic关键字是访问说明符。 它表明该变量可以从外界完全访问。

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 具有五个访问修饰符:PublicProtectedPrivateFriendProtectedFriendPublic成员可以从任何地方访问。 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)

PublicProtected成员由Derived类继承。 可以访问它们。 Private成员未继承。 访问成员字段的行被注释。 如果我们取消注释该行,它将无法编译。

$ ./protected.exe 
This is Derived Class
Members inherited:
Base
5323

运行程序,我们收到此输出。 PublicProtected成员是继承的,Private成员则不是。

方法重载

方法重载允许创建多个具有相同名称的方法,它们的输入类型彼此不同。

方法重载有什么好处? Qt4 库提供了一个很好的用法示例。 QPainter类具有三种绘制矩形的方法。 它们的名称为drawRect(),其参数不同。 一个引用一个浮点矩形对象,另一个引用一个整数矩形对象,最后一个引用四个参数,xywidthheight。 如果开发 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

我们有四个类。 继承层次更加复杂。 HumanAnimal类继承自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继承自BeingDog继承自动物。 Dog也间接继承自Being

Dim h As New Human
Dim d As New Dog
d.GetCount

我们从HumanDog类创建实例。 我们称为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,它是按钮小部件的抽象基类,提供按钮所共有的功能。 按钮Q3ButtonQCheckBoxQPushButtonQRadioButtonQToolButton都从此基本抽象类继承。

正式地说,抽象类用于强制执行协议。 协议是一组操作,所有实现对象都必须支持。

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

原文: https://zetcode.com/lang/visualbasic/oopii/

在 Visual Basic 教程的这一章中,我们将继续以 Visual Basic 语言描述 OOP。

接口

遥控器是观众和电视之间的接口。 它是此电子设备的接口。 外交礼仪指导外交领域的所有活动。 道路规则是驾车者,骑自行车者和行人必须遵守的规则。 编程中的接口类似于前面的示例。

接口是:

  • API
  • 合约

对象通过这些方法与外界交互。 实际的实现对程序员而言并不重要,或者也可能是秘密的。 公司可能会出售图书馆,但它不想透露实际的实现情况。 程序员可能会在 GUI 工具箱的窗口中调用Maximize方法,但对如何实现此方法一无所知。 从这个角度来看,接口是一种方法,通过这些方法,对象可以与外界交互,而不会过多地暴露其内部工作原理。

从第二个角度来看,接口就是契约。 如果达成协议,则必须遵循。 它们用于设计应用的架构。 他们帮助组织代码。

接口是完全抽象的类型。 它们使用Interface关键字声明。 接口只能具有方法签名和常量。 接口中声明的所有方法签名必须是公共的。 他们不能具有完全实现的方法,也不能具有成员字段。 Visual Basic 类可以实现任何数量的接口。 一个接口还可以扩展任何数量的接口。 实现接口的类必须实现接口的所有方法签名。

接口用于模拟多重继承。 Visual Basic 类只能从一个类继承。 Visual Basic 类可以实现多个接口。 使用接口的多重继承与继承方法和变量无关。 它是关于继承想法或合同的,这些想法或合同由接口描述。

接口和抽象类之间有一个重要的区别。 抽象类为继承层次结构中相关的类提供部分实现。 另一方面,可以通过彼此不相关的类来实现接口。 例如,我们有两个按钮。 经典按钮和圆形按钮。 两者都继承自抽象按钮类,该类为所有按钮提供了一些通用功能。 实现类是相关的,因为它们都是按钮。 另一个示例可能具有类DatabaseSignIn。 它们彼此无关。 我们可以应用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类。 该类变形为两个后代类,即RectangleSquare。 两者都提供了自己的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

RectangleSquare类具有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方法。 编译器为每种形状调用正确的方法。 这就是多态的本质。

NotOverridableNotInheritable

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

这是一个浅表副本的示例。 我们定义了两个自定义对象:MyObjectColorMyObject对象将具有对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。大小不同。 smallbig。 但是,这两个实例的颜色对象的红色部分相同:255。更改克隆对象的成员值(IdSize)不会影响原始对象。 更改引用对象(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 值。

异常

异常是为处理异常的发生而设计的,这些特殊情况会改变程序执行的正常流程。 引发或引发异常。

在执行应用期间,许多事情可能出错。 磁盘可能已满,我们无法保存文件。 互联网连接可能断开,我们的应用尝试连接到站点。 所有这些都可能导致我们的应用崩溃。 为避免发生这种情况,我们必须应对可能发生的所有可能的错误。 为此,我们可以使用异常处理。

TryCatchFinally关键字用于处理异常。

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基类。 CountTime成员是将被初始化并随事件携带的数据。

If Random = 5 Then
    Count += 1
    args = New FiveEventArgs(Count, Now)
    RaiseEvent ValueFive(Me, args)
End If

如果生成的随机数等于 5,我们用当前的CountDate值实例化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 中的面向对象编程。

posted @ 2024-10-24 18:16  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报