VB.net学习笔记(十二)继承中的构造、事件、共享方法、共享事件


例子源码: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


       因此这三个继承链上的类,构造情况如图:

        VB.net学习笔记(十二)继承中的构造、事件、共享方法、共享事件_第1张图片


          

         因此,继承链上的各类构造函数如果带参数,须仔细考虑上下级类的构造函数带参情况。


         仔细看一下构造中的流程:

         VB.net学习笔记(十二)继承中的构造、事件、共享方法、共享事件_第2张图片

        子类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

       

            运行,执行顺序如下:

 VB.net学习笔记(十二)继承中的构造、事件、共享方法、共享事件_第3张图片


            可以看到,事件是可以继承的。


           结论:

           子类可以访问其基类中的事件,但子类中的代码不能直接用代码引发该事件

           上例中不能用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

         显示为:False、True。因为第一个调用的Person版本的比较,第二调用的是Employee版本的比较。

         如果上面重载方法移动至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包装后,调用方法间接引发。











你可能感兴趣的:(继承,VB.NET,构造)