VB.NET(Visual Basic 7)具备了面向对象(OOP)编程语言的所有特征。面向对象的概念和面向对象编程方式对于VB编程人员看来并不陌生。
问一个面向对象程序设计高手面向对象程序设计语言是什么,他可能会说出一大堆诸如类、接口、消息隐匿、封装、继承、多态性这样的名词,这些名称听起来酷,但面向对象编程并非通过一两天的学习或听一次课就能掌握的。要真正地掌握面向对象程序设计,不但需要掌握一定的理论知识,同时还要进行一些实际的编程练习。接下来将探讨在VB.NET中运用面向对象原理编程的基本方法。
面向对象编程的优点
不知道读者是否考虑过为什么现代程序设计语言会向面向对象编程靠拢?C++、JAVA为什么这么普及?这是因为面向对象编程具备了几个优点,比如:代码维护方便、可扩展性好、支持代码重用技术等等。这些优点是过程编程语言所不具备的。下面我们就来谈谈面向对象技术的这些优点:
维护简单
模块化是面向对象编程中的一个特征。实体被表示为类和同一名字空间中具有相同功能的类,我们可以在名字空间中添加一个类而不会影响该名字空间的其他成员。
可扩充性
面向对象编程从本质上支持扩充性。如果有一个具有某种功能的类,就可以很快地扩充这个类,创建一个具有扩充的功能的类。
代码重用
由于功能是被封装在类中的,并且类是作为一个独立实体而存在的,提供一个类库就非常简单了。事实上,任何一个.NET Framework编程语言的程序员都可以使用.NET Framework类库,.NET Framework类库提供了很多的功能。更令人高兴的是,我们可以通过提供符合需求的类来扩充这些功能。
下面我们最简单的特性开始来论述面向对象编程的一些特性。
类
在面向对象编程技术中,类是重点中的重点。简单地说,类是一种提供一定功能的数据类型。在VB.NET中定义一个类要用到关键字Class,例如,下面的一小段代码就定义一个名字为Employee的类:
Employee类
Class Employee
End Class
定义一个类就是这么简单。注意,在对类命名时,微软推荐使用Pascal语言的命名规则。根据这种命名规则,就意味着类名的第一个字母必须大写,并且后面的并发连结词的第一个字母均为大写,例如象GeneralManager、SmallDictionary、StringUtil都是合乎这种规则的类名。
类成员
一个类有象域、属性、子程序和函数这些成员,例如,下面的employee类中有一个名字为work的子程序:
包含Work方法的Employee类
Class Employee
Public Sub Work ()
' Do something here
End Sub
End Class
子程序和函数都被称为方法,方法的命名也遵循Pascal语言的命名规则。
另一种类成员是域。域的命名法则遵循camel规则,即除第一个子串外的所有子串的第一个字母大写。象salary和quarterlyBonus都是符合规则的域名。下面的代码在Employee类中添加了salary和quarterlyBonus这二个域:
增加了二个域的Employee类
Class Employee
Dim salary As Decimal = 40000
Dim yearlyBonus As Decimal = 4000
Public Sub PrintSalary()
' print the salary to the Console
System.Console.Write(salary)
End Sub
End Class
Module Module1
Public Sub Main()
Dim anEmployee As Employee
anEmployee = New Employee()
anEmployee.PrintSalary()
End Sub
End Module
上述代码段中的Module1模块中提供了子程序的Main函数,这也是VB.NET程序开始的地方。要编译源程序,就必须用一种或另一种方式提供访问Main Sub的途径。
如果你使用的不是Visual Studio.NET,那么可以使用vbc.exe软件编译VB.NET源程序,vbc.exe是在安装.NET Framework时自动安装的。例如,当你把源代码保存为Employee.vb文件后,在Employee.vb所在的目录下,输入vbc Employee.vb即可编译该源程序。
现在我们再来看看上面的代码,子程序的Main函数首先定义了一个Employee类型的变量━━anEmployee:
Dim anEmployee As Employee
然后使用关健词New对Employee进行初始化:
anEmployee = New Employee()
这样,我们就得到了一个Employee类型的变量,我们就可以使用它的功能了(福特汽车公司的工程师生产出汽车后,我们就可以启动并驾驶它了。)。在我们的例子中,可以使用下面的方法调用PrintSalary方法:
anEmployee.PrintSalary()
这一方法会打印Employee中salary变量的值。
当然,我们也可以将子程序的Main函数移到类的定义中去,这样就无需再使用模块了。下面的代码即演示了这种方法: 子程序的Main函数在类的定义中
Class Employee
Dim salary As Decimal = 40000
Dim yearlyBonus As Decimal = 4000
Public Sub PrintSalary()
' print the salary to the Console
System.Console.Write(salary)
End Sub
Public Shared Sub Main()
Dim employee As Employee
employee = New Employee()
employee.PrintSalary()
End Sub
End Class
注释:PrintSalary方法中的System.Console.Write表示我们调用了Console类中的Write方法,而Console类又是System名字空间的一部分。关于名字空间的要领将在下面的部分讨论:
名字空间
在编写.NET软件时,我们会用到类和其他类型。为了使应用程序更有条理性,可以将类组合为名字空间,微软的.NET Framework类库就是这样的。如果打开.NET Framework SDK文档中的.NET Framework Class Library,会看到其中有80多个名字空间,需要经常乃至的重要的名字空间包括System、System.IO、System.Drawing、System.Windows.Forms等。例如,在Employee类的PrintSalary方法中,我们就使用了System名字空间中的Console类。
如果要在程序中使用名字空间,可以首先导入它,以便在以后每次使用其成员时无需重复该名字空间的名字。例如:可以将表4、5中的代码改写为下面表6中形式:
导入名字空间
Imports System
Class Employee
Dim salary As Decimal = 40000
Dim yearlyBonus As Decimal = 4000
Public Sub PrintSalary()
' print the salary to the Console
Console.Write(salary)
End Sub
Public Shared Sub Main()
Dim employee As Employee
employee = New Employee()
employee.PrintSalary()
End Sub
End Class
好了,我们现在就可以在PrintSalary方法使用Console类而无需引用名字空间,因为我们已经导入这个名字空间了。
我们还可以在不同的名字空间中拥有相同名字的类。要正确地使用一个类,通常的做法是在一个类的前面使用名字空间的名字。例如,可以以System.Console的方式使用System名字空间中的Console类。
访问类型
在很多情况下,我们都会将编写好的类提供给别人,供他们使用它提供的功能,例如,他们可以调用类的一个方法或者访问其中的一个域。面向对象编程的一个最大的好处是开发人员可以方便地控制对类成员的访问,这意味着我们可以完全控制想让别人使用的部分。我们可以使一个方法可以被别的开发人员使用,也可以使一个类成员只能在该类中被访问。 在VB.NET中,访问是分等级的。下面我们来讨论这些等级:
pPublic:Public类成员没有访问限制。在一个类成员前面添加Public关健字就使得它可以被随意访问。例如,Employee类中的PrintSalary方法就是一个public方法,可以从任何地方对它进行访问。
Private:秘密的类成员只能被该类内部的其他成员访问。使用Private关健字就可以使一个类成员成为秘密的。
Protected:被保护的类成员只能被该类的派生类和该类本身内部进行访问。使用Protected关健字就可以使类成员成为被保护的类成员。
Friend:具有friend级访问限制的类成员只能在定义该类的程序内部使用,使用Friend关健字就能使类成员具有friend级访问限制。
Protected friend:这是protected和friend二种访问类型的组合。这些不同的访问类型使面向对象编程具有了信息隐慝能力。也就是说,我们可以使用这些访问类型保护不愿意让别人访问的信息。
静态成员
我们再来看看表4、5、6中的Employee类,也许读者会对我们没有将System.Console类实例化就使用它的Write有点不理解,为什么我们可以这样做呢?因为在面向对象编程语言中,有一种被称作静态成员的特殊的类成员,VB.NET也有静态成员这一概念。
无需对一个对象实例化就可以使用其中的静态成员。例如,下面的表7中,SalaryLevel类中就只包含有静态的域:
类中的表态成员
Class SalaryLevel
Public Shared Level1 As Decimal = 35000
Public Shared Level2 As Decimal = 40000
Public Shared Level3 As Decimal = 45000
Public Shared Level4 As Decimal = 50000
Public Shared Level5 As Decimal = 55000
Public Shared Level6 As Decimal = 60000
Public Shared Level7 As Decimal = 65000
Public Shared Level8 As Decimal = 70000
Public Shared Level9 As Decimal = 75000
Public Shared Level10 As Decimal = 80000
End Class
我们可以象表8中的程序所演示的那样在程序中使用类:
Listing 8: Using a static member of a class
Imports System
Class SalaryLevel
Public Shared Level1 As Decimal = 35000
Public Shared Level2 As Decimal = 40000
Public Shared Level3 As Decimal = 45000
Public Shared Level4 As Decimal = 50000
Public Shared Level5 As Decimal = 55000
Public Shared Level6 As Decimal = 60000
Public Shared Level7 As Decimal = 65000
Public Shared Level8 As Decimal = 70000
Public Shared Level9 As Decimal = 75000
Public Shared Level10 As Decimal = 80000
End Class
Class Employee
Dim yearlyBonus As Decimal = 4000
Public Sub PrintSalary()
'使用SalaryLevel类的静态域向Console输出工资额
Console.Write(SalaryLevel.Level4)
End Sub
Public Shared Sub Main()
Dim employee As Employee
employee = New Employee()
employee.PrintSalary()
End Sub
End Class
在Employee类的PrintSalary方法中,我们可以在不首先创建SalaryLevel类变量的情况下使用其中的静态域Level4。不属于静态成员的类成员被称作实例成员。
构造器
A构造器是类初始化所必须使用的特殊方法,在VB.NET中,这一方法被称作New。但我们在前面的代码中可以发现,在类中我们没有定义New这个方法。正是这样,如果类中没有定义构造器,VB.NET将自动地创建一个构造器,当使用关健字New对对象进行初始化时,也就调用了类的构造器。当然,我们也可以自己编写对象在初始化时所运行的代码。
如果我们在程序中创建了构造器,VB.NET就不会再自动为该类创建构造器了。
继承
继承是扩展类的一种特性。如果需要完成一些功能,当然可以创建一个新类,但如果别人所创建的类可以提供一部分你所需要的功能,就可以创建一个扩充了原有类的新类,我们创建的类可以称为子类或派生类,原来的类可以被称为基础类或父类。有时,子类和继承也用于描述对类的扩充。在VB.NET中,一个类只能继承一个父类,多类继承在VB.NET中是不允许的。
从语法上说,在类名后加一个冒号,后面再加上关健字Inherits和父类的名字就可以完成对类的继承。例如,下面表9中的代码就通过扩充Employee类创建了一个被称为Manager的新类:
扩充类
Imports System
Class Employee
Dim salary As Decimal = 40000
Dim yearlyBonus As Decimal = 4000
Public Sub PrintSalary()
' print the salary to the Console
Console.Write(salary)
End Sub
End Class
Class Manager: Inherits Employee
End Class
如果关健字出现在下一行上,则子类名后面的分号就不需要了,如下面的代码所示:
Class Manager
Inherits Employee
End Class
现在,我们就可以初始化一个Manager对象,并使用Employee中的成员。如下面表10中的代码所示:
初始化Manager对象
Class Employee
Public salary As Decimal = 40000
Public yearlyBonus As Decimal = 4000
Public Sub PrintSalary()
' print the salary to the Console
Console.Write(salary)
End Sub
End Class
Class Manager: Inherits Employee
End Class
Module Module1
Public Sub Main()
Dim manager As Manager
manager = New Manager()
manager.PrintSalary()
End Sub
End Module
下面表11中的代码演示了如何通过编写一个新的PrintBonus方法来扩充Manager类别的方法:
在子类中添加新的方法
Class Manager: Inherits Employee
Public Sub PrintBonus()
Console.Write(yearlyBonus)
End Sub
End Class
注意成员可访问性限制的使用。例如,如果使yearlyBonus域具有private属性,该哉就不能被Manager类访问,因此,编译这样的代码就会发生错误。
继承是面向对象编程中常用的方法。实际上,.NETFramework类库中就有许多由继承其他类得到的类。例如,Windows.Forms名字空间中的Button类就是ButtonBase类的一个子类,而ButtonBase类本身又是Control类的一个子类。所有的类最终都是System.Object类的子类,在.NET Framework类库中,System.Object类被称为根或超级类。
表12中的代码演示了继承的强大功能:
扩充System.Windows.Forms.Form
Public Class MyForm : Inherits System.Windows.Forms.Form
End Class
这是一个空的类定义,在被编译和运行时,就会显示一个Windows窗体。看看,无需编写一行代码我们就可以创建一个窗体,这是因为MyForm是由System.Windows.Forms.Form继承生成的,它继承了Form类的功能。
不可继承的类
我们可以通过使用NotInheritable关健字使自己的类不可被别人继承。例如,表13中的Calculator就是不可继承的:
不可继承的类
NotInheritable Class Calculator
End Class
如果扩充这个类就会引起编译错误。为什么会使我们的类不可继承呢?一个原因是不希望别人扩充我们的类,另一个原因是不可扩充的类产生的代码运行速度更快。尽管这样,我们还是应该小心地使用不可继承的类,因为它不符合面向对象编程的初衷,只有在100%地肯定不扩充这个类时,才能使它不可继承。
在有些面向对象编程语言中,这些类也被称作最终的类。
写在最后
VB.NET支持许多面向对象编程的特征。本篇文章讨论了VB.NET中一些基本的面向对象特征,希望能够使广大读者能够对VB.NET中面向对象编程特征有一个基本的认识,并起到一个抛砖引玉的作用。