第八章 Visual Basic,NET面向对象程序设计开发知识点:
类和对象对象的生命周期继承接口多态性自从推出 Visual Basic 4.0版本以来,Visual Basic已具备了强大的面向对象的功能,VB.NET更大大增强了这些功能,还支持完全面向对象的语言所需要的 4个主要概念:抽象性、封装性、多态性和继承性。下面将在本章具体讨论这些概念。
8.1 概 述过程式程序设计是一种自上而下、逐层细化的设计方式,在过程式编程中,用一个 main函数来概括整个系统,在 main函数中,通过一系列的控制流程和嵌套的函数调用,最终完成整个系统的功能。
在这个过程中,函数是处理问题和划分功能的基本单位,通过自上而下的设计,上层函数当需要完成某个子功能时,只需要调用相应的子函数而不需要知道子函数的具体实现方式。这种逐层细化的方法使得过程设程序设计的程序结构更加优雅。
图展示了一个典型的过程式程序面向对象式程序设计一种自下而上的程序设计方法。面向对象设计往往从问题的一部分着手,一层一层的逐步描述出整个系统的全貌。面向对象设计以数据为中心(过程式设计以操作为中心),
数据(属性)和对数据的操作(方法)共同构成类,一种数据对应一系列的操作,这样就克服了过程式编程中数据的操作分布在系统的各个位置的缺点,也使得数据的操作更加安全。只需要类的设计员来保证数据的操作合法性。
客户( Client)关系继承关系
8.2 类很久以来,VB因为缺乏完善的面向对象支持、缺乏高效的错误处理机制和性能表现不佳。 VB.NET彻底改变了这种情况。
VB.NET提供了完整的面向对象支持,包括封装
( Encapsulation),继承( Inheritance)和多态
( Polymorphism),是一种真正的面向对象编程语言。
封装是指对象只将需要公开的方法和属性公开,而隐藏外部不需要了解的方法和属性。这种设计方式将一些细节的问题隐藏了起来。使得系统的开发变得更加有层次结构和简单。举个例子来说,一辆汽车,驾驶员只需要知道踩油门的时候汽车会启动并前进,而不需要知道踩油门的时候汽车内部的机械装置以及燃料等的工作过程,这样,对于驾驶员来说问题就变得非常简单了,甚至在多年以后,驾驶员都不需要知道,其实汽车的设计者早已经更改了汽车内部的工作过程。
封装
8.2.1 类的声明如何使用vb。Net声明 一个类要声明一个类需要以下步骤:
( 1)单击“文件”菜单上的“新建项目”,创建一个项目。“新建项目”对话框随即出现,如图所示。
( 2)从 Visual Basic项目模板列表中选择,Windows应用程序”,以显示新项目。在“项目”菜单中单击“添加类”按钮,将一个新类添加到项目中,弹出如图所示对话框。
单击添加新类,弹出如图所示对话框,选择要添加的类。
( 3)给新模块 firstclass.vb命名,然后单击“打开”按钮以显示新类的代码,如图所示。
( 4)单击标签中的 Form1.vb[设计 ]标签,切换到设计窗体,在窗体上添加一个 Button按钮和一个 TextBox文本框,均采用默认名称 Button1和
TextBox1
( 5)在 class框架内添加如下代码:
Private str As String
Public Sub output()
str = Form1.TextBox1.Text
Console.WriteLine(str)
End Sub
( 6)运行,在文本框内输入,MY FIRST CLASS”,单击,Button1”按钮,结果如图所示。
VB.net使用 Class关键字来定义类,其格式如下:
修饰符 Class 类名。
End Class。
Public
声明语句中的 Public( Visual Basic)关键字表明可从以下位置访问元素:同一项目中任意位置的代码,引用该项目的其他项目,以及由该项目生成的任何程序集。
Protected
声明语句中的 Protected( Visual Basic)关键字表明仅可以从同一个类内部或从该类派生的类中访问元素。
Friend
声明语句中的 Friend( Visual Basic)关键字表明可以从同一程序集内部访问元素,而不能从程序集外部访问。
Protected Friend
声明语句中的 Protected和 Friend关键字一同出现时,表明可从以下位置访问元素:派生类或同一程序集内,或两者皆可。
Private
声明语句中的 Private( Visual Basic)关键字表明仅可以从同一模块、类或结构内访问元素。
修饰符包括 Public Private Protect Friend等,它声明元素的访问级别
8.2.2 类的成员类的成员包括属性( Property),方法( Method),字段( Field),
事件( Event),以及构造类的构造器( Constructor)。
类的成员可以是以下两种类型之一:静态成员,实例成员。
实例成员依赖于该类创建的实例,当重新创建一个类的一个实例的时候,系统会在内存中为该实例的每一个实例成员分配内存空间,同一个类的多个实例之间的存储地址是不一样的。实例成员依赖于具体的实例对象。
静态成员(也称为共享成员)是该类的所有实例(对象)共享的成员,与实例成员不同的是类为自己的所有静态成员维护一个统一的版本,当通过类名或实例名引用该类的静态成员的时候,实际上都是引用的同一个内存地址上的同一个值。
8.2.3 类和命名空间微软公司推出的,Net FrameWork SDK是专门为,Net程序开发语言提供的一个内容庞大的软件开发包。其中基本元素就是命名空间
( NameSpace),名称空间实质上是一个大的类库( Class Library)。在其中定义了许多的类、对象、属性和方法
8.2.4 创建字段和属性可以使用字段和属性在对象中存储信息。虽然从客户端应用程序角度来看,
字段和属性几乎无法区别,但在类中声明它们的方式不同。字段只是类的简单变量,而属性使用 Property 过程控制如何设置或返回值。
1,向类添加字段在类定义中声明一个变量,如下面的代码所示:
Public Class firstclass
Public str As String
End Class
在代码中将可以如下所示的使用 str字段。
Dim frt As firstclass
frt.str=value?赋值或变量名 =frt.str?取值使用字段存储信息时,无法智能的控制用户的输入,而且在创建可视化组件的时候,用户无法在属性设置器中进行设置,所以凡是创建与用户交互频繁的字段时,一般都设置为属性。
2,向类添加属性在类中声明一个局部变量来存储属性值。因为属性不会自行分配任何存储区,所以该步骤是必需的。若要保护它们的值不被直接修改,应当将用于存储属性值的变量声明为 Private。
根据需要以修饰符(如 Public和 Shared)作为属性声明的开头。使用
Property关键字声明属性名称,并声明属性存储和返回的数据类型。
在属性定义内定义 Get和 Set属性过程。 Get属性过程用于返回属性值,其在语法上与函数大致等效。它们不接受参数,并可用于返回私有局部变量的值,这些变量在类中声明并用于存储属性值。 Set属性过程用于设置属性值;它们有一个参数(通常称为 Value),其数据类型与属性本身相同。每当属性值更改时,Value均会被传递给 Set属性过程,在该过程中可以验证它并将其存储在一个局部变量中。
使用相应的 End Get和 End Set语句终止 Get和 Set属性过程。
使用 End Property语句终止属性块。
8.2.5 创建方法
“方法”是包含一系列语句的代码块。在 VB中,每个执行指令都是在方法的上下文中完成的。方法在类中声明,声明时需要指定访问级别、返回值、方法名称以及任何方法参数。方法参数放在括号中,并用逗号隔开。空括号表示方法不需要参数。方法是对用户公开的过程,过程放在起始声明语句( Sub或
Function)和结束声明语句( End Sub 或 End Function)之间。过程的所有代码均位于这些语句之间。一个过程中不能包含其他过程,因此其起始和结束语句必须位于其他任何过程之外。也就是说,方法是具有 Public访问级别的过程。
1,创建不返回值的过程在其他任何过程之外,使用一条 Sub语句,然后是一条 End Sub语句。
在 Sub语句中,Sub关键字的后面为过程名,随后是位于括号中的参数列表。
过程的代码语句放在 Sub语句与 End Sub语句之间。
2,创建返回值的过程在其他任何过程之外,使用一条 Function语句,后跟一条 End Function语句。
在 Function语句中,Function关键字的后面为过程名,紧接着是位于括号中的参数列表,然后是指定返回值的数据类型的 As子句。
将过程的代码语句放在 Function语句与 End Function语句之间。
使用 Return语句将值返回给调用代码。
8.2.6 创建事件
1,声明事件声明事件的时候使用的关键字是 Event,只允许在类、接口或结构的内部声明事件,声明示例如下:
Event AnEvent(ByVal EventArgument As Object)
2,引发事件广播消息的行为称为“引发”事件。在 Visual Basic.Net中,使用
RaiseEvent 语句引发事件,引发事件后,系统将会自动调用该事件对应的所有事件处理程序,如下面的示例所示:
RaiseEvent AnEvent(object)
引发事件只能在声明该事件的类、模块或结构的范围之内,并且在子类中也不能引发基类中的事件。
3,事件发送器任何能引发事件的对象都是事件发送方,也称“事件源”。注意,这里的事件源并不是指鼠标等直观上引发事件的对象,而是在程序体中,
引发事件的代码所处的对象。窗体、控件和用户定义的对象都可以是事件源。
4,事件处理程序
5,关联事件与事件处理程序
“事件处理程序”是当事件发生的时候将要调用的过程。在为事件添加处理程序的时候,事件处理程序的签名必须与事件的签名匹配,
在事件发生的时候,系统如何知道有哪些过程是该事件的处理程序呢?
这就需要我们在 事件发生之前将事件与该事件的处理程序关联起来,使用 Handles或 AddHander可以将事件与事件处理程序关联。
使用 WithEvent语句和 Handles语句可以在声明过程的时候将该过程作为事件处理程序与对应的事件关联起来。
使用 AddHandler和 RemoveHandler可以在运行的时候将事件和过程关联起来,这种方式更为灵活,因为这种方式不要求在声明过程的时候进行关联,而是在运行中动态的建立关系或删除关系。
8.2.7 共享方法和共享成员共享方法,也称静态方法。一个共享方法不能作为一种普通的方法,通过一个对象的实例来访问,而是可以从类直接访问。一个共享方法可以使用
Shared关键字来声明。
Public Class Math
Shared Function Add(ByVal a As Integer,ByVal b As Integer) _
As Integer?共享方法
Return a + b
End Function
End Class
一个共享方法的简单例子:
8.2.8 重载方法通过对方法进行重载,可以创建几个同名的方法,这些方法都具有不同的形参列表,每一个都接受一组不同的参数或不同数据类型的参数。
举个简单的例子,在 output方法中,不使用 Optinal关键字,而使用重载。
原来的 output方法仍保持不变,但添加另一个 output方法,它接受不同的参数列表。在 firstclass类中修改代码:
Public Sub output(Byval pstr As String)
Str=pstr
Console.WriteLine(str)
End Sub
然后创建一个同名的方法,但这个方法携带不同的参数列表(在本例中是不带参数)。在不删除或改变现有的 output方法的情况下,将下列代码添加到 firstclass类中:
Public Sub output()
Str=“hello word”
Console.WriteLine(str)
End Sub
现在就有两个 output方法了。把它们区分开的惟一方式是它们的参数列表不同。第一个 output方法需要一个 String参数,第二个不带参数。
8.3 对象的生命周期
8.3.1 创建对象和取消引用对象
1,创建对象在 VB6中是使用 CreatObject创建一个对象的。例如:
Dim obj As Person
obj = New Person()
我们可以简化上面的语句:
Dim obj As New Person()
在 VB.NET中,上面的两段语句之间是没有什么区别的,只是第二段语句缩短了而已 。
2,构造函数事实上,在定义类时,VB.NET 默认一个名为 New的构造函数,是一个始终被调用的特定的方法。对于初始化代码来说,构造函数是一个理想的存储单元,因为它总是在调用任何其他方法之前运行。
但是,经常在创建对象的时候往往要用数据来初始化对象。可以从数据库中来装载一些数据,或者可以直接为对象提供数据。不管用什么方法,是想在对象被创建的时候为它提供一些数据。为了做到这点,可以增加参数列表给 New方法:
Public Class firstclass
Public Sub New(ByVal ID As Integer)?在这里使用 ID数值来初始化对象
Intval=ID
End Sub
End Class
现在来创建类的一个实例,并且为对象提供数据,代码如下:
Dim obj As New firstclass(12)
为了增加灵活性,可以接收可选的参数数值。为了实现这个,可以有两种方法:通过使用 Optional关键字来声明一个可选择的参数,或者通过重载 New方法。为了使用
Optional关键字,简单地声明可选择的参数,代码如下:
Public Sub New(Optional ByVal ID As Integer=-1)
If ID = -1 Then
Intval=0
Else
Intval=ID
End If
End Sub
3,取消对象引用在处理完对象后,可以通过取消对象的引用,来表明对该对象的使用完毕。
为了取消对象的引用,只需将对象引用设置为 Nothing即可:
Dim obj As firstclass
obj=New firstclass()

Obj=Nothing
一旦所有引用对象的变量都设置为 Nothing,.Net运行时就会销毁该对象,回收该对象占用的内存和资源。
在取消对象引用和,Net实际销毁该对象之间的这段时间,对象位于内存中。在销毁对象之前,Framework会为该对象调用 Finalize方法。
Finalize方法会在后面的对象的终止一节作介绍。
8.3.2 对象的使用
Visual Basic的一个强大功能是在处理对象的时候可以实现前期和后期绑定。
前期绑定:
指代码在直接处理对象时提前知道数据类型,并且可以更有效地处理对象。
后期绑定:
代码在允许的时候动态的处理对象。
CType函数的使用
CType函数用来将一个未知类型的对象转换为一个特定类型的对象,使用
CType转换对象的时候可以实现对象的前期绑定。
8.3.3 对象的终止
1.,Net中的内存回收机制
.NET平台提供了许多新功能来帮助程序员优化代码和简化开发,其中之一就是垃圾回收器( GC)。
垃圾回收器对窗口句柄或打开的文件和流等非托管资源一无所知。为解决这个问题,.NET提供了 IDisposable接口,将此接口的 Dispose 方法与垃圾回收器一起使用来显式释放非托管资源。当不再需要对象时,对象的使用者可以调用此方法。
2,Dispose方法需要进行显示清理的对象需实现 IDisposable接口。 IDisposable接口提供了
Dispose方法,该方法不像 Finalize方法,它可以在开发者的控制之下显示的调用来清理对象。
既然 Dispose方法的调用是显示清理,使用垃圾收集器收集这些对象没有必要。
因此 Dispose方法应该包含一个对 GC.SuppressFinalize()的调用,提示垃圾收集器在这个对象上不需要使用 Finalize方法。
8.4 继 承继承的思想是创建一个类,重用其他类中的方法、属性、事件和变量可以创建一个具有基类的通用功能,还具备新的、增强的或甚至完全改变的功能。
继承是指一个新类以一个现有类为基础,从原始类中继承其接口和功能。前面探讨过类和对象间的关系 ——类本质上是一个模板,在该模板上可以创建对象。
在继承关系中,被继承的类成为父类(也成基类),继承类称为子类。
继承本质上是一种将现有类的功能合并到新子类中的方式。继承也定义了合并方法、属性和事件的规则,包括控制如何改变或代替它们,以及子类如何为其自身增加新的方法、属性和事件。
8.4.1 基本继承使用 VB.NET创建一个新的 Windows Application项目。接着使用项目 \添加类菜单选项,在项目中添加一个类,将其命名为 Graphic.vb。
从下列代码开始:
Public Class Graphic
EndClass
此时从技术的角度上来讲,有了一个基类,现在可以像平常一样为这个类添加方法、属性各事件,基于 Graphic创建的任何类都会继承所有这些接口元素。
Inherits关键字用于表示类应派生于一个现有的类 ——从该类中继承了接口和行为。可以继承项目,NET系统类库或其他程序集中几乎所有的类。
Public Class Circle Inherits Graphic
Private circleX As Integer?圆心的 X坐标
Private circleY As Integer?圆心的 Y坐标
Private Diam As Integer?直径
Public Property circle_x() As Integer '圆心的 X坐标属性过程
Get
Return circleX
End Get
Set(ByVal Value As Integer)
circleX = Value
End Set
End Property
Public Property circle_y() As Integer '圆心的 Y坐标属性过程
Get
Return circleY
End Get
Set(ByVal Value As Integer)
circleY = Value
End Set
End Property
Public Property diamoand ()As Integer?直径属性过程
Get
Return Diam
End Get
Set(ByVal Value As Integer)
Diamoand = Value
End Set
End Property
End Class
Circle类通过继承自动获得了 lineColor,fillColor属性和 draw方法,它还拥有自己的属性 ——circleX,circleY及 diamoand属性。这就说明了可以给 Circle子类添加方法和属性,来扩展基本的 Graphic接口。
8.4.2 限制继承有时,需要在基类中创建既能被子类也能被基类调用的方法,但类外部的代码不能调用这些方法。也就是说,需要的是具有 Public和 Private混合功能的方法 —
—这些方法对继承链中的类来说是私有的,但在继承内创建的子类可以利用这些方法。 Protected作用域就可以提供这种功能。
Protected方法和 Private方法非常相似,因为任何调用对象的代码都不能使用
Protected方法,而基类内部的代码可以使用它们,子类中的代码也可以。
Protected作用域可以应用于 Sub,Function和 Property方法。为了说明
Propected作用域是如何工作的,下面将 Graphic类 fillcolor属性的修饰符 Private改为 Protect:
Protect Property fillColor() As color '充添颜色属性过程
Get
Return fill_color
End Get
Set(ByVal Value As color)
Fill_color= Value
End Set
End Property
这不影响 Circle对象及继承于 Graphic类的派生类的对象使用 fillcolor属性,但在继承链外之外的其他类却不能直接使用 fillcolor属性。例如,如果想在窗体的代码中使用它,就会发现 Graphic,Circle对象没有 fillcolor属性。
8.4.3 跨语言继承
Visual Basic.NET可以创建处理代码,这些处理代码是运行在,NET框架上的。所有的托管代码可以和其他类型的处理代码交互作用,而不管是用什么编程语言来创建这些组件的。这是因为当使用,NET来开发的时候,不管使用的是 VB还是 C#或者是托管 C++,系统编译它们的时候都是编译成 IL代码的,这种 IL代码是运行在,NET
Framework运行时框架上的,类似于 Java的字节码,而,NET Framework运行时框架就相当于 Java的虚拟机。从这个层次上来说,这就意味着开发人员可以在一种编程语言上创建一个类,然后应用到另外的编程语言中,当然也包括继承实际上,现在有许多程序设计人员已经在应用这个技术了。许多的,NET系统类库是在 C#上编写的,而在 Visual Basic.NET上编写程序的时候,可以继承这部分类。
创建 Visual Basic.NET基类创建 C#子类
8.4.4 可视化继承
Visual Basic.NET的继承特性不仅局限于代码方式的隐性继承,而且还支持
Windows窗体的可视化继承。例如,可以从 Form类继承而创建一个新的
MyForm类,在这个类中,可以通过可视化的修改使这个 MyForm类拥有一些自己的特色,如颜色、现状等。
开发人员还可以使用继承来创建自己的 Windows控件。例如,创建一个性能提高了的 TextBox来实现数据输入特殊的有效性校验。具体的做法是:通过继承创建一个由原始文本框控件类继承而来的子类,并且进行适当的修改以提高文本框的性能,可以实现对数据输入的有效性校验,例如只能输入数字的文本框。这一点跟 Web窗体控件是相同的,Web窗体控件可以由一个已经存在的 Web窗体控件来创建一个子类。子类可以重载已有的函数或者增加一些新的函数。在第 13章的组件编程介绍时大家会有更深的理解。
8.5 接 口接口是一种约定,在定义接口的时候只定义出接口要实现的功能,而不需要具体的编写出实现接口的代码,实现接口的类必须严格按其定义来实现接口的方法。
8.5.1 定义接口在项目的任何代码模块中,可以使用 Interface关键字定义一个正式接口,但最好将这种类型的定义放在标准模块里。接口定义了一组方法( Sub Function
或 Property)和事件。选择实现该接口的类必须实现这些方法和事件。
Public Interface Run_object
Public Sub Run ()
End Interface
这里设计了一个会跑的对象接口 Run_some,它有一个未实现的方法 Run,
需要注意的是必须用 Public或 Friend作用域来声明接口,声明一个 Private或
Protected接口会导致语法错误。同样可以实现一个居住类的接口和一个艺术品类的接口:
Public Interface estate
Property area () As Double?面积属性
Property position () As String?位置属性
Property quality () As String?质量属性
End Interface
Public Interface art_object
Property style () As Strng?风格属性
Property shape () As String?造型属性
Property Technique () As String?技巧属性
End Interface
8.5.2 实现接口任何类(除了抽象基类)都可以使用 Implement关键字来实现接口。例如:
Public Class horse
Implement Run_object
Public Sub Run ( ) Implement Jump_object.Jump
实现代码
End Sub
End Class
同理,还可以创建实现 Run_object接口的 car类:
Public Class car
Implement Run_object
Public Sub Run ( ) Implement Jump_object.Jump
实现代码
End Sub
End Class
8.6 多态性多态性常常和继承结合在一起考虑。但实际上,它们在很大程度上是独立的。多态性指的是两个类可以具有不同的实现代码,但有一组相同方法、
属性或事件,这样可以编写一个操作接口的程序,而不必关心在运行期间它操作的是哪一种类型的对象。
在学习多态性之前,必须了解几个相关概念:方法签名和抽象基类
8.6.1 方法签名要真正地理解多态性,首先要介绍方法签名的概念,方法签名有时也叫做函数原型。所有的方法都有一个签名,通过学习方法名及其参数的数据类型来定义。
例如,下面的代码:
Public Sub output()
在该示例中,签名是 f()
如果在该方法中添加一个参数,其签名就会改变。例如,将该方法改变为接受 Double类型值:
Public Sub output(Byval Dblnum As Double)
则该方法的签名为,f(Double)
多态性是说可以编写一些客户代码来调用对象的方法,只要该对象给方法提供了所需的方法签名,就不用考虑该对象是从哪一个类中创建的。
8.6.2 抽象基类在任何继承于基类的子类中,必须重写以这种方式声明的方法。如果不重写其中的任意一个方法,就会在子类中生成一个语法错误,子类也不会编译。
利用 MustInherit和 MustOverride创建抽象基类,抽象基类有时也称为虚拟类。
在某些方面,抽象基类与使用 Interface关键字来定义接口是非常相似的。
8.6.3 实现多态性在面向对象的设计和编程中,多态性是一个非常重要的概念,在 VB.NET中提供了许多技术来实现多态性:后期绑定、接口,NET反射和继承等。
前面已经介绍过的后期绑定实际上是以降低性能和编程的便利性为代价来实现纯多态性的。通过学习多接口和继承也可以实现多态性,其性能更好,编程也更容易。反射既可以使用后期绑定,也可以使用多接口,但不能使用动态创建的对象,甚至不能在运行期间将 DLL动态加载至应用程序中,以便使用其类。因此日常应用中更多的使用接口和继承来实现多态性。