例子源码:http://download.csdn.net/detail/dzweather/5916173
(复习备用)
一、构造函数
继承对构造函数方法的影响与对常规方法的影响不同。
1、不带参的构造函数
用New来创建构造函数。它于创建对象时才运行一次。
构造函数不会有Overridable,Overrides,因为对象都没创建,无法进行重写,所以如果使用这两个关键字会产生语法错误。
所有不带参数的构造函数,写与不写,都不会影响继承。
2、带参的构造函数
带参的构造函数,在继承中变得复杂。因为子类的元素继承来自基类,如果基类都构造,那么子类不可能产生。
先看一下无参时:
实际上子类构造函数中第一行代码是调用的是基类的构造函数。
这个必须是第一行,并且只能占用一行。
如果第一行不是这个显式地写出,则在后台,VB.net会插入一个对父类构造函数的有效调用。下例是手动显式写出:
无参在注释里。(Employee类中)
Public Sub New() MyBase.New("George") 'MyBase.New() Debug.WriteLine("Employee Constructor") End Sub
构造函数也称ctor,在ILDASM或.NET Reflector工具中经常用到这个术语。
在继承链中,每个构造函数总将调用MyBase.New作为第一行,以保证本类的上一级得到有效的构造。
再结合看一下带参的情况:
怎么把参数带给上一级呢? 上例中第一行用带参方法向上一级。使得上一级能正确使用参数构造。
但上例用的是“具体”的参数值,这叫“硬编码”,硬编码方式限制了程序的通用性,可用一些变量来增加程序的通用性:
Public Sub New(ByVal name As String) MyBase.New(name) Debug.WriteLine("Employee Constructor") End Sub(上例Employee类中)
配合子类Employee传来的name参数,基类Person的构造函数如下:
Public Sub New(ByVal name As String) Me.Name = name Debug.WriteLine("Person Constructor") End Sub
但上例中出现了一个严重的错误:
Person中的Name属性被子类Employee重写且重载了。上面调用的是重写版本。
重写版本在子类中,该版本不能使用Dictionary!!!
因为类中任何使用New语句声明的成员变量,如Employee中Dictionary对象,只能在执行完该类的构造函数后,才会被初始化。
这时正在执行Person的构造,所以无法执行Employee的构造,似乎是个死结。。。。
为了解决此问题,须修改Employee类,应去掉字段中的New,改为在方法或属性中执行。
修改如下:
Private mNames As Generic.Dictionary(Of NameType, String) Public Overloads Property Name(ByVal type As NameType) As String Get If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameType, String) Return mNames(type) End Get Set(value As String) If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameType, String) If mNames.ContainsKey(type) Then mNames.Item(type) = value Else mNames.Add(type, value) End If If type = NameType.normal Then MyBase.Name = value End If End Set End Property
但这样又引发了OfficeEmploy中构造的错误,因为Employe中的参数name也要来自它的子类OfficeEmployee,
故OfficeEmployee的构造函数:
Public Sub New(ByVal name As String) MyBase.New(name) Debug.WriteLine("OfficeEmployee Constructor") End Sub
因此这三个继承链上的类,构造情况如图:
因此,继承链上的各类构造函数如果带参数,须仔细考虑上下级类的构造函数带参情况。
仔细看一下构造中的流程:
子类custom进入构造时,将调用父类Peron的构造,构造中会对New进行初始化,所以对mName初始化。父类构造完成后
将返回子类中继续构造,这时对子类中的mName进行初始化,子类完成后,就完成了继承链中上的构造。
二、Protected
Protected是Pulic与Private的混合品种,对类外它Private,对类内(继承链)它是Public。
Friend 仅用于项目或组件中的代码
Protected Friend 派生类或项目内,或者两者绋可
Protected Friend—Available to code within your project/component and classes that inherit from the class whether in the project or not.
下面在Person增加如下Protected成员:
Private mID As String Protected Property Identity() As String Get Return mID End Get Set(value As String) mID = value End Set End Property
子类Employee添加:
Public Property EmployeeNumber() As Integer Get Return CInt(Identity) End Get Set(value As Integer) Identity = value End Set End Property可以看到继承后,在类内Protected成员可以象Public那样直接使用。
Protected对类外的对象是不可见的,因此很好完成封装效果控制。
三、继承中的事件
在 Person类添加事件:
Private mName As String Public Event NameChanged(ByVal newName As String) Public Overridable Property Name() As String Get Return mName End Get Set(value As String) mName = value RaiseEvent NameChanged(mName) End Set End Property
Employ类及OfficeEmployee类的Name属性:
'============Employee类中Name属性代码================= Private mNames As Generic.Dictionary(Of NameType, String) Public Overloads Property Name(ByVal type As NameType) As String Get If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameType, String) Return mNames(type) End Get Set(value As String) If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameType, String) If mNames.ContainsKey(type) Then mNames.Item(type) = value Else mNames.Add(type, value) End If If type = NameType.normal Then MyBase.Name = value End If End Set End Property Public Overloads Overrides Property Name() As String Get Return Name(NameType.normal) End Get Set(value As String) Name(NameType.normal) = value End Set End Property '=============OfficeEmployee类中Name属性代码============ Public Shadows Property Name() As String Get Return MyBase.Name(NameType.informal) End Get Set(value As String) MyBase.Name = value End Set End Property
Public Class Form1 Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click Dim temp As Employee = New officeEmployee("zheng") AddHandler temp.NameChanged, AddressOf OnNameChanged With temp temp.Name = "Fred" End With End Sub Private Sub OnNameChanged(ByVal newName As String) MsgBox("Name Changed:" & newName) End Sub End Class
运行,执行顺序如下:
可以看到,事件是可以继承的。
结论:
子类可以访问其基类中的事件,但子类中的代码不能直接用代码引发该事件。
上例中不能用Employee或OfficeEmployee中的RaiseEvent方法来引用NameChanged事件。(尽管Person中定义事件为Public),只有Person才能用RaiseEvent引发本类定义的事件。
下例是Employee中用RaseEvent使用Person中的事件,将出错:
Private mSalary As Double Public Property Salary() As Double Get Return mSalary End Get Set(value As Double) mSalary = value 'RaiseEvent DataChanged("Salary", value) '出错,子类不能直接用语句引发基类事件 End Set End Property
但,可用Protected把该语句包装成方法后,供后面子类调用该方法,从而间接激发:
'==========基类Person用Protected方法来包装RaiseEvent================ Protected Sub OnDataChanged(ByVal field As String, ByVal value As Object) RaiseEvent DataChanged(field, value) End Sub '==========子类Employee中用调用方法的形式间接激发事件================== Public Property Salary() As Double Get Return mSalary End Get Set(value As Double) mSalary = value 'RaiseEvent DataChanged("Salary", value) '子类不能直接用语句引发基类事件 OnDataChanged("Salary", value) End Set End Property
这样,在子程序分别定义好接收事件,并关联好事件:
Public Class Form1 Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click Dim temp As Employee = New officeEmployee("zheng") AddHandler temp.NameChanged, AddressOf OnNameChanged AddHandler temp.DataChanged, AddressOf OnDataChanged '关联到事件 temp.Name = "Fred" temp.Salary = 300 End Sub Private Sub OnNameChanged(ByVal newName As String) MsgBox("Name Changed:" & newName) End Sub Protected Sub OnDataChanged(ByVal field As String, ByVal newValue As Object) '事件处理 MsgBox("New " & field & ": " & CStr(newValue)) End Sub End Class注意的是主程序中接收事件OnDataChanged与类中的OnDateChanged是不一样的。
因为类中的是Protected它只能在类内使用,不能对外。
四、共享方法
共享方法是类在加载时就被加载到内存中的方法,在整个运行过程中保持不变,因而不能重写。
但非共享方法是在对象实例化时才单独申请内存空间,为每一个实例分配独立的运行内存,因而可以重写。
同时注意到共享方法是类加载(而不是对象实例化)时就产生,且固定了内存位置,而非共享方法是对象实例化时再
分配内存空间,其内存的地址是随即产生,无法定向,所以共享方法不能访问非共享方法。按照C++的说法,就是共享
方法是没有This指针的。
共享方法可以被继承,也可以被重载或隐藏,但不能重写!!
下例用类名调用共享比较方法:
'==============Person类中的共享方法,添加代码===================== Public Shared Function Compare(ByVal person1 As Person, ByVal person2 As Person) As Boolean Return person1.Name = person2.Name End Function '==============主程序中调用程序=================== Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click Dim emp1 As New Employee("Fred") Dim emp2 As New Employee("Mary") MsgBox(Person.Compare(emp1, emp2)) '继承共享方法 End Sub
继承与重载共享方法
继续添加,以便共享方法在子类Employee中重载(类型不一样):
'==============Employee类中重载共享方法===================== Public Overloads Shared Function Compare(ByVal employee1 As Employee, ByVal employee2 As Employee) As Boolean Return employee1.EmployeeNumber = employee2.EmployeeNumber End Function '==============主程序中调用程序========== Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click Dim emp1 As New Employee("Fred") Dim emp2 As New Employee("Mary") emp1.EmployeeNumber = 1 emp2.EmployeeNumber = 1 MsgBox(Person.Compare(emp1, emp2)) '继承共享方法 MsgBox(Employee.Compare(emp1, emp2)) '重载比较 End Sub
如果上面重载方法移动至OfficeEmploy中时,显示结果将是:False,False
因为它们两个都会调用Person中的Compare方法,所以结果一样。
隐藏共享方法
虽然不能重写,但可隐藏,在OfficeEmployee中定义一个与父类Employee方法签名一样的共享方法
'==============OfficeEmploy类中隐藏共享方法===================== Public Shared Shadows Function Compare(ByVal person1 As Person, ByVal person2 As Person) As Boolean Return person1.Age = person2.Age End Function '==============主程序中调用程序========== Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click Dim emp1 As New Employee("Fred") Dim emp2 As New Employee("Mary") emp1.Age = 25 emp2.Age = 20 MsgBox(officeEmployee.Compare(emp1, emp2)) End Sub结果:显示False
尽管对象是Employee,例共享方法是通过类名调用,这里用的是OfficeEmployee,故用它的共享方法。
虽然它也继承了上一级Employee的共享方法,但因隐藏原因,将使用本类的共享方法。
五、共享事件
1、共享事件可以被继承。象前面事件一样。
2、同理子类中不能用RaiseEvent来引发父类的共享事件。只能象前面一样用Protected包装后,调用方法间接引发。