Visual Basic 中的面向对象编程 II
最后修改于 2023 年 10 月 18 日
在本 Visual Basic 教程中,我们将继续介绍 Visual Basic 语言中的 OOP。
接口
遥控器是观众和电视之间的接口。它是这个电子设备的接口。外交礼仪指导外交领域的所有活动。交通规则是驾车者、骑自行车者和行人必须遵守的规则。编程中的接口类似于前面的例子。
接口是
- API
- 约定
对象通过它们公开的方法与外部世界交互。实际的实现对程序员来说并不重要,或者也可能保密。一家公司可能会出售一个库,并且不希望披露实际的实现。程序员可能会在 GUI 工具包的窗口上调用 Maximize 方法,但对该方法是如何实现的知之甚少。从这个角度来看,接口是方法,对象通过它们与外部世界交互,而不会过多地暴露其内部工作原理。
从第二个角度来看,接口是合同。如果达成一致,就必须遵守。它们用于设计应用程序的架构。它们有助于组织代码。
接口是完全抽象的类型。它们使用 Interface 关键字声明。接口只能有方法签名和常量。在接口中声明的所有方法签名都必须是公共的。它们不能有完全实现的方以及成员字段。一个 Visual Basic 类可以实现任意数量的接口。一个接口也可以扩展任意数量的接口。实现接口的类必须实现接口的所有方法签名。
接口用于模拟_多重继承_。Visual Basic 类只能从一个类继承。一个 Visual Basic 类可以实现多个接口。使用接口进行多重继承不是关于继承方法和变量。它涉及继承接口描述的想法或合同。
接口和抽象类之间有一个重要的区别。抽象类为在继承层次结构中相关的类提供部分实现。另一方面,接口可以由彼此不相关的类实现。例如,我们有两个按钮。一个经典的按钮和一个圆形按钮。两者都从一个抽象的按钮类继承,该类为所有按钮提供了一些通用功能。实现类是相关的,因为它们都是按钮。另一个例子可能包含类 Database 和 SignIn。它们彼此不相关。我们可以应用一个 ILoggable 接口,该接口将强制它们创建一个用于执行日志记录的方法。
Option Strict On
Module Example
Interface IInfo
Sub DoInform()
End Interface
Class Some
Implements IInfo
Sub DoInform() Implements IInfo.DoInform
Console.WriteLine("This is Some Class")
End Sub
End Class
Sub Main()
Dim sm As New Some
sm.DoInform()
End Sub
End Module
这是一个简单的 Visual Basic 程序,演示了一个接口。
Interface IInfo
Sub DoInform()
End Interface
这是一个接口 IInfo。它具有 DoInform 方法签名。
Class Some
Implements IInfo
我们使用 Implements 从接口实现。
Sub DoInform() Implements IInfo.DoInform
Console.WriteLine("This is Some Class")
End Sub
该类为 DoInform 方法提供了一个实现。Implements 关键字显式地指定了我们正在实现的方法签名。
下一个例子展示了一个类如何实现多个接口。
Option Strict On
Module Example
Interface Device
Sub SwitchOn()
Sub SwitchOff()
End Interface
Interface Volume
Sub VolumeUp()
Sub VolumeDown()
End Interface
Interface Pluggable
Sub PlugIn()
Sub PlugOff()
End Interface
Class CellPhone
Implements Device, Volume, Pluggable
Public Sub SwitchOn() Implements Device.SwitchOn
Console.WriteLine("Switching on")
End Sub
Public Sub SwitchOff() Implements Device.SwitchOff
Console.WriteLine("Switching on")
End Sub
Public Sub VolumeUp() Implements Volume.VolumeUp
Console.WriteLine("Volume up")
End Sub
Public Sub VolumeDown() Implements Volume.VolumeDown
Console.WriteLine("Volume down")
End Sub
Public Sub PlugIn() Implements Pluggable.PlugIn
Console.WriteLine("Plugging In")
End Sub
Public Sub PlugOff() Implements Pluggable.PlugOff
Console.WriteLine("Plugging Off")
End Sub
End Class
Sub Main()
Dim o As New CellPhone
o.SwitchOn()
o.VolumeUp()
o.PlugIn()
End Sub
End Module
我们有一个 CellPhone 类,它继承自三个接口。
Class CellPhone
Implements Device, Volume, Pluggable
该类实现了所有三个接口,这些接口用逗号分隔。CellPhone 类必须实现所有三个接口的所有方法签名。
$ dotnet run 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 接口继承,该接口由类实现。
$ dotnet run This is DBConnect class Version 1.02 Logging Connecting to the database
多态
多态性是针对不同的数据输入以不同方式使用运算符或函数的过程。实际上,多态性意味着如果类 B 从类 A 继承,它不必继承关于类 A 的所有内容;它可以以不同的方式执行类 A 所做的一些事情。(维基百科)
一般来说,多态性是能够以不同形式出现的能力。从技术上讲,它是在派生类中重新定义方法的能力。多态性涉及将特定实现应用于接口或更通用的基类。
多态性是重新定义派生类方法的能务。
Option Strict On
Module Example
MustInherit Class Shape
Protected x As Integer
Protected y As Integer
Public MustOverride Function Area() As Integer
End Class
Class Rectangle
Inherits Shape
Sub New(ByVal x As Integer, ByVal y As Integer)
Me.x = x
Me.y = y
End Sub
Public Overrides Function Area() As Integer
Return Me.x * Me.y
End Function
End Class
Class Square
Inherits Shape
Sub New(ByVal x As Integer)
Me.x = x
End Sub
Public Overrides Function Area() As Integer
Return Me.x * Me.x
End Function
End Class
Sub Main()
Dim shapes() As Shape = { New Square(5),
New Rectangle(9, 4), New Square(12) }
For Each shape As Shape In shapes
Console.WriteLine(shape.Area())
Next
End Sub
End Module
在上面的程序中,我们有一个抽象的 Shape 类。这个类变形为两个后代类,Rectangle 和 Square。两者都提供了它们自己的 Area() 方法的实现。多态性为 OOP 系统带来了灵活性和可伸缩性。
Public Overrides Function Area() As Integer
Return Me.x * Me.y
End Function
...
Public Overrides Function Area() As Integer
Return Me.x * Me.x
End Function
Rectangle 和 Square 类具有它们自己的 Area 方法的实现。
Dim shapes() As Shape = { New Square(5),
New Rectangle(9, 4), New Square(12) }
我们创建一个包含三个形状的数组。
For Each shape As Shape In shapes
Console.WriteLine(shape.Area())
Next
我们遍历每个形状,并调用其上的 Area 方法。编译器为每个形状调用正确的方法。这是多态性的本质。
NotOverridable,NotInheritable
NotOverridable 方法不能被重写,而 NotInheritable 类不能被继承。这些关键字是应用程序设计的问题。我们不应该从某些类继承,并且不应该重写某些方法。
Option Strict On
Module Example
Class Base
Public NotOverridable Sub Say()
Console.WriteLine("Base class")
End Sub
End Class
Class Derived
Inherits Base
Public Overrides Sub Say()
Console.WriteLine("Derived class")
End Sub
End Class
Sub Main()
Dim o As Base = New Derived
o.Say()
End Sub
End Module
此程序将无法编译。我们收到错误“Public Overrides Sub Say()”无法重写“Public NotOverridable Sub Say()”,因为它被声明为“NotOverridable”。
Option Strict On
Module Example
NotInheritable Class Math
Public Shared Function getPI() As Single
Return 3.141592
End Function
End Class
Class DerivedMath
Inherits Math
Public Sub Say()
Console.WriteLine("DerivedMath class")
End Sub
End Class
Sub Main()
Dim o As DerivedMath = New DerivedMath
o.Say()
End Sub
End Module
在上面的程序中,我们有一个原型基类 Math。这个类的唯一目的是为程序员提供一些有用的方法和常量。(在我们的例子中,为了简单起见,我们只有一个方法。)它不是为了被继承而创建的。为了防止其他不知情的程序员从这个类派生,创建者使该类为 NotInheritable。如果你尝试编译这个程序,你会收到以下错误:'DerivedMath' 无法从类 'Math' 继承,因为 'Math' 被声明为 'NotInheritable'。
深拷贝与浅拷贝
复制数据是编程中的一项重要任务。对象是 OOP 中的复合数据类型。对象中的成员字段可以按值或按引用存储。复制可以通过两种方式执行。
浅拷贝将所有值和引用复制到新实例中。引用指向的数据不会被复制;只有指针被复制。新的引用指向原始对象。对引用成员的任何更改都会影响这两个对象。
深拷贝将所有值复制到新实例中。对于作为引用存储的成员,深拷贝将对被引用的数据执行深拷贝。将创建一个被引用对象的新副本。并且将存储指向新创建对象的指针。对这些被引用对象的任何更改都不会影响对象的其他副本。深拷贝是完全复制的对象。
如果成员字段是值类型,则执行字段的逐位复制。如果字段是引用类型,则复制引用,但不会复制被引用的对象;因此,原始对象中的引用和克隆中的引用都指向同一个对象。(programmingcorner.blogspot.com 提供了一个清晰的解释)
接下来的两个例子对对象执行浅拷贝和深拷贝。
Option Strict On
Module Example
Class Color
Public red as Byte
Public green as Byte
Public blue as Byte
Sub New(red As Byte, green As Byte,
blue As Byte)
Me.red = red
Me.green = green
Me.blue = blue
End Sub
End Class
Class MyObject
Implements ICloneable
Public Id As Integer
Public Size As String
Public Col As Color
Sub New(Id As Integer, Size As String,
Col As Color)
Me.Id = Id
Me.Size = Size
Me.Col = Col
End Sub
Public Function Clone() As Object
Implements ICloneable.Clone
Return New MyObject(Me.Id, Me.Size, Me.Col)
End Function
Public Overrides Function ToString() As String
Dim s As String
s = String.Format("Id: {0}, Size: {1}, Color:({2}, {3}, {4})",
Me.Id, Me.Size, Me.Col.red, Me.Col.green, Me.Col.blue)
Return s
End Function
End Class
Sub Main()
Dim col As New Color(23, 42, 223)
Dim obj1 As New MyObject(23, "small", col)
Dim obj2 As MyObject
obj2 = CType(obj1.Clone(), MyObject)
obj2.Id += 1
obj2.Size = "big"
obj2.Col.red = 255
Console.WriteLine(obj1)
Console.WriteLine(obj2)
End Sub
End Module
这是一个浅拷贝的例子。我们定义了两个自定义对象:MyObject 和 Color。MyObject 对象有一个对 Color 对象的引用。
Class MyObject
Implements ICloneable
我们应该为要克隆的对象实现 ICloneable 接口。
Public Function Clone() As Object
Implements ICloneable.Clone
Return New MyObject(Me.Id, Me.Size, Me.Col)
End Function
ICloneable 接口强制我们创建一个 Clone 方法。此方法返回一个具有复制值的新对象。
Dim col As New Color(23, 42, 223)
我们创建了 Color 对象的一个实例。
Dim obj1 As New MyObject(23, "small", col)
创建了 MyObject 对象的一个实例。它将 Color 对象的实例传递给它的构造函数。
obj2 = CType(obj1.Clone(), MyObject)
我们创建了 obj1 对象的浅拷贝,并将其分配给 obj2 变量。Clone 方法返回一个 Object,我们期望的是 MyObject。这就是我们进行显式类型转换的原因。
obj2.Id += 1 obj2.Size = "big" obj2.Col.red = 255
在这里,我们修改了已复制对象的成员字段。我们增加了 Id,将 Size 更改为 "big",并更改了 color 对象的红色部分。
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 vs 24。Size 是不同的。"small" vs "big"。但是 color 对象的红色部分对于这两个实例来说是相同的:255。更改克隆对象(Id、Size)的成员值不会影响原始对象。更改被引用对象(Col)的成员也影响了原始对象。换句话说,这两个对象都指向内存中的同一个颜色对象。
要更改此行为,我们接下来进行深拷贝。
Option Strict On
Module Example
Class Color
Implements ICloneable
Public Red as Byte
Public Green as Byte
Public Blue as Byte
Sub New(Red As Byte, Green As Byte,
Blue As Byte)
Me.Red = Red
Me.Green = Green
Me.Blue = Blue
End Sub
Public Function Clone() As Object
Implements ICloneable.Clone
Return New Color(Me.Red, Me.Green, Me.Blue)
End Function
End Class
Class MyObject
Implements ICloneable
Public Id As Integer
Public Size As String
Public Col As Color
Sub New(Id As Integer, Size As String,
Col As Color)
Me.Id = Id
Me.Size = Size
Me.Col = Col
End Sub
Public Function Clone() As Object
Implements ICloneable.Clone
Return New MyObject(Me.Id, Me.Size, CType(Me.Col.Clone(), Color))
End Function
Public Overrides Function ToString() As String
Dim s As String
s = String.Format("Id: {0}, Size: {1}, Color:({2}, {3}, {4})",
Me.Id, Me.Size, Me.Col.Red, Me.Col.Green, Me.Col.Blue)
Return s
End Function
End Class
Sub Main()
Dim col As New Color(23, 42, 223)
Dim obj1 As New MyObject(23, "small", col)
Dim obj2 As MyObject
obj2 = CType(obj1.Clone(), MyObject)
obj2.Id += 1
obj2.Size = "big"
obj2.Col.Red = 255
Console.WriteLine(obj1)
Console.WriteLine(obj2)
End Sub
End Module
在这个程序中,我们对对象执行深拷贝。
Class Color
Implements ICloneable
现在,Color 类实现了 ICloneable 接口。
Public Function Clone() As Object
Implements ICloneable.Clone
Return New Color(Me.Red, Me.Green, Me.Blue)
End Function
我们也有一个 Color 类的 Clone 方法。这有助于创建被引用对象的副本。
Public Function Clone() As Object
Implements ICloneable.Clone
Return New MyObject(Me.Id, Me.Size, CType(Me.Col.Clone(), Color))
End Function
现在,当我们克隆 MyObject 时,我们对 Col 引用类型调用 Clone 方法。通过这种方式,我们也有了颜色值的副本。
$ dotnet run Id: 23, Size: small, Color:(23, 42, 223) Id: 24, Size: big, Color:(255, 42, 223)
现在,被引用的 Color 对象的红色部分不再相同。原始对象保留了其之前的 23 值。
异常
异常旨在处理异常的发生,异常是改变程序正常流程的特殊条件。异常被引发或抛出,被初始化。
在我们的应用程序执行期间,很多事情可能会出错。磁盘可能已满,我们无法保存我们的文件。互联网连接可能中断,我们的应用程序尝试连接到一个站点。所有这些都可能导致我们的应用程序崩溃。为了防止这种情况发生,我们必须应对所有可能发生的错误。为此,我们可以使用异常处理。
Try、Catch 和 Finally 关键字用于处理异常。
Option Strict On
Module Example
Sub Main()
Dim x As Integer = 100
Dim y As Integer = 0
Dim z As Double
Try
z = x \ y
Catch e As Exception
Console.WriteLine(e.Message)
End Try
End Sub
End Module
在上面的程序中,我们故意将一个数字除以零。这会导致一个错误。
Try
z = x \ y
...
End Try
容易出错的语句放在 Try 关键字之后。
Catch e As Exception
Console.WriteLine(e.Message)
...
异常类型跟在 Catch 关键字之后。在我们的例子中,我们有一个通用的 Exception,它将捕获任何类型的异常。有一些通用的异常和一些更具体的。在 Catch 关键字之后的语句在发生错误时执行。当发生异常时,会创建一个异常对象。从这个对象中,我们获取 Message 属性并将其打印到控制台。
当前上下文中任何未捕获的异常都会传播到更高的上下文,并寻找合适的 catch 块来处理它。如果它找不到任何合适的 catch 块,.NET 运行时的默认机制将终止整个程序的执行。
Option Strict On
Module Example
Sub Main()
Dim z As Double
Dim x As Integer = 100
Dim y As Integer = 0
z = x \ y
End Sub
End Module
在这个程序中,我们除以零。我们没有自定义的异常处理。我们收到以下错误消息:未处理的异常: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
在这个例子中,我们捕获了各种异常。请注意,更具体的异常应在通用异常之前。我们从控制台读取两个数字,并检查零除错误和数字格式错误。
$ dotnet run 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
大于此常量的数字被我们的程序认为是“大”数字。
Sub New(ByVal msg As String)
MyBase.New(msg)
End Sub
在构造函数内部,我们调用父类的构造函数。我们将消息传递给父级。
If (x > LIMIT) Then
Throw New BigValueException("Exceeded the maximum value")
End If
如果该值大于限制,我们将抛出我们的自定义异常。我们给异常一个消息“超过最大值”。
Catch e As BigValueException
Console.WriteLine(e.Message)
我们捕获异常并将其消息打印到控制台。
属性(Properties)
属性是特殊的类成员。我们使用预定义的 set 和 get 方法来访问和修改它们。属性的读写会转换为 get 和 set 方法调用。使用字段表示法访问变量(例如 object.Name)比使用自定义方法调用更容易(例如 object.GetName)。但是,使用属性,我们仍然具有封装和信息隐藏的优势。
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 类的一个实例。我们使用字段表示法访问成员字段。
$ dotnet run 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 方法的引用。现在我们指向了方法,我们可以通过委托调用它。
$ dotnet run 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 关键字。
$ dotnet run 0 1 5 Five Event occured 2 5 Five Event occured 6 7 6 3 3 1
接下来,我们有一个更复杂的例子。
Option Strict On
Namespace EventSample
Public Class FiveEventArgs
Inherits EventArgs
Public Count As Integer
Public Time As Date
Public Sub New(ByVal Count As Integer, ByVal Time As Date)
Me.Count = Count
Me.Time = Time
End Sub
End Class
Public Class Five
Private Count As Integer = 0
Public Sub OnFiveEvent(ByVal source As Object, ByVal e As FiveEventArgs)
Console.WriteLine("Five event {0} occured at {1}", e.Count, e.Time)
End Sub
End Class
Public Class RandomGenerator
Public Event ValueFive(ByVal source As Object, ByVal e As FiveEventArgs)
Public Sub Generate()
Dim Count As Integer = 0
Dim args As FiveEventArgs
For i As Byte = 0 To 10
Dim Random As Integer
Randomize()
Random = CInt(Rnd * 6)
Console.WriteLine(Random)
If Random = 5 Then
Count += 1
args = New FiveEventArgs(Count, Now)
RaiseEvent ValueFive(Me, args)
End If
Next
End Sub
End Class
Public Class Example
Public Shared Sub Main()
Dim five As New Five
Dim gen As New RandomGenerator
AddHandler gen.ValueFive, AddressOf five.OnFiveEvent
gen.Generate()
End Sub
End Class
End Namespace
我们有四个类。FiveEventArgs 携带事件对象的一些数据。Five 类封装了事件对象。RandomGenerator 类负责随机数生成。它是事件发送者。最后是 Example 类,它是主应用程序对象,并且具有 Main 方法。
Public Class FiveEventArgs
Inherits EventArgs
Public Count As Integer
Public Time As Date
...
FiveEventArgs类在事件对象中携带数据。它继承自 EventArgs 基类。Count 和 Time 成员是将被初始化并随事件携带的数据。
If Random = 5 Then
Count += 1
args = New FiveEventArgs(Count, Now)
RaiseEvent ValueFive(Me, args)
End If
如果生成的随机数等于 5,我们使用当前的 Count 和 Date 值来实例化 FiveEventArgs 类。Count 变量计算此事件生成的次数。Time 值保存事件生成的时间。事件使用 RaiseEvent 关键字,连同发送者对象和事件参数一起发送。
AddHandler gen.ValueFive, AddressOf five.OnFiveEvent
我们将 ValueFive 事件连接到其处理程序。
$ dotnet run 5 Five event 1 occured at 9/3/2022 1:07:41 PM 5 Five event 2 occured at 9/3/2022 1:07:41 PM 4 6 5 Five event 3 occured at 9/3/2022 1:07:41 PM 4 5 Five event 4 occured at 9/3/2022 1:07:41 PM 1 5 Five event 5 occured at 9/3/2022 1:07:41 PM 4 3
在本 Visual Basic 教程的这一部分中,我们继续讨论了 Visual Basic 中的面向对象编程。