分享

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

 百眼通 2014-10-31


例子源码:http://download.csdn.net/detail/dzweather/5916173

(复习备用)




一、构造函数


        继承对构造函数方法的影响与对常规方法的影响不同。


        1、不带参的构造函数

         用New来创建构造函数。它于创建对象时才运行一次。

         构造函数不会有Overridable,Overrides,因为对象都没创建,无法进行重写,所以如果使用这两个关键字会产生语法错误。


         所有不带参数的构造函数,写与不写,都不会影响继承。



        

           2、带参的构造函数

          带参的构造函数,在继承中变得复杂。因为子类的元素继承来自基类,如果基类都构造,那么子类不可能产生。


          先看一下无参时:

          实际上子类构造函数中第一行代码是调用的是基类的构造函数。

          这个必须是第一行,并且只能占用一行。

         如果第一行不是这个显式地写出,则在后台,VB.net会插入一个对父类构造函数的有效调用。下例是手动显式写出:

         无参在注释里。(Employee类中)

  1. Public Sub New()  
  2.      MyBase.New("George") 'MyBase.New()  
  3.      Debug.WriteLine("Employee Constructor")  
  4.  End Sub  

         构造函数也称ctor,在ILDASM或.NET Reflector工具中经常用到这个术语。


         在继承链中,每个构造函数总将调用MyBase.New作为第一行,以保证本类的上一级得到有效的构造。


          再结合看一下带参的情况:

          怎么把参数带给上一级呢?  上例中第一行用带参方法向上一级。使得上一级能正确使用参数构造。

          但上例用的是“具体”的参数值,这叫“硬编码”,硬编码方式限制了程序的通用性,可用一些变量来增加程序的通用性:

  1. Public Sub New(ByVal name As String)  
  2.      MyBase.New(name)  
  3.      Debug.WriteLine("Employee Constructor")  
  4.  End Sub  
         (上例Employee类中)


         配合子类Employee传来的name参数,基类Person的构造函数如下:

  1. Public Sub New(ByVal name As String)  
  2.     Me.Name = name  
  3.     Debug.WriteLine("Person Constructor")  
  4. End Sub  

          这样就完成了带参数函数的匹配。


         但上例中出现了一个严重的错误:

         Person中的Name属性被子类Employee重写且重载了。上面调用的是重写版本。

         重写版本在子类中,该版本不能使用Dictionary!!!

         因为类中任何使用New语句声明的成员变量,如Employee中Dictionary对象,只能在执行完该类的构造函数后,才会被初始化。


         这时正在执行Person的构造,所以无法执行Employee的构造,似乎是个死结。。。。

        为了解决此问题,须修改Employee类,应去掉字段中的New,改为在方法或属性中执行。

       修改如下:

  1. Private mNames As Generic.Dictionary(Of NameType, String)  
  2.   
  3. Public Overloads Property Name(ByVal type As NameType) As String  
  4.     Get  
  5.         If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameType, String)  
  6.         Return mNames(type)  
  7.     End Get  
  8.     Set(value As String)  
  9.         If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameType, String)  
  10.         If mNames.ContainsKey(type) Then  
  11.             mNames.Item(type) = value  
  12.         Else  
  13.             mNames.Add(type, value)  
  14.         End If  
  15.         If type = NameType.normal Then  
  16.             MyBase.Name = value  
  17.         End If  
  18.     End Set  
  19. End Property  


       但这样又引发了OfficeEmploy中构造的错误,因为Employe中的参数name也要来自它的子类OfficeEmployee,

       故OfficeEmployee的构造函数:

  1. Public Sub New(ByVal name As String)  
  2.     MyBase.New(name)  
  3.     Debug.WriteLine("OfficeEmployee Constructor")  
  4. 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成员:

  1. Private mID As String  
  2. Protected Property Identity() As String  
  3.     Get  
  4.         Return mID  
  5.     End Get  
  6.     Set(value As String)  
  7.         mID = value  
  8.     End Set  
  9. End Property  


      子类Employee添加:

  1. Public Property EmployeeNumber() As Integer  
  2.     Get  
  3.         Return CInt(Identity)  
  4.     End Get  
  5.     Set(value As Integer)  
  6.         Identity = value  
  7.     End Set  
  8. End Property  
       可以看到继承后,在类内Protected成员可以象Public那样直接使用。

       Protected对类外的对象是不可见的,因此很好完成封装效果控制。








三、继承中的事件


      在 Person类添加事件:

  1. Private mName As String  
  2.   
  3. Public Event NameChanged(ByVal newName As String)  
  4.   
  5. Public Overridable Property Name() As String  
  6.     Get  
  7.         Return mName  
  8.     End Get  
  9.     Set(value As String)  
  10.         mName = value  
  11.         RaiseEvent NameChanged(mName)  
  12.     End Set  
  13. End Property  


        Employ类及OfficeEmployee类的Name属性:

  1. '============Employee类中Name属性代码=================  
  2.   
  3.     Private mNames As Generic.Dictionary(Of NameType, String)  
  4.   
  5.     Public Overloads Property Name(ByVal type As NameType) As String  
  6.         Get  
  7.             If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameType, String)  
  8.             Return mNames(type)  
  9.         End Get  
  10.         Set(value As String)  
  11.             If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameType, String)  
  12.             If mNames.ContainsKey(type) Then  
  13.                 mNames.Item(type) = value  
  14.             Else  
  15.                 mNames.Add(type, value)  
  16.             End If  
  17.             If type = NameType.normal Then  
  18.                 MyBase.Name = value  
  19.             End If  
  20.         End Set  
  21.     End Property  
  22.   
  23.     Public Overloads Overrides Property Name() As String  
  24.         Get  
  25.             Return Name(NameType.normal)  
  26.         End Get  
  27.         Set(value As String)  
  28.             Name(NameType.normal) = value  
  29.         End Set  
  30.     End Property  
  31.   
  32. '=============OfficeEmployee类中Name属性代码============  
  33.   
  34.     Public Shadows Property Name() As String  
  35.         Get  
  36.             Return MyBase.Name(NameType.informal)  
  37.         End Get  
  38.         Set(value As String)  
  39.             MyBase.Name = value  
  40.         End Set  
  41.     End Property  


          主程序中添加接收事件:

  1. Public Class Form1  
  2.     Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click  
  3.         Dim temp As Employee = New officeEmployee("zheng")  
  4.   
  5.         AddHandler temp.NameChanged, AddressOf OnNameChanged  
  6.         With temp  
  7.             temp.Name = "Fred"  
  8.         End With  
  9.     End Sub  
  10.   
  11.     Private Sub OnNameChanged(ByVal newName As String)  
  12.         MsgBox("Name Changed:" & newName)  
  13.     End Sub  
  14. End Class  

       

            运行,执行顺序如下:

 


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


           结论:

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

           上例中不能用Employee或OfficeEmployee中的RaiseEvent方法来引用NameChanged事件。(尽管Person中定义事件为Public),只有Person才能用RaiseEvent引发本类定义的事件。


          下例是Employee中用RaseEvent使用Person中的事件,将出错:

  1. Private mSalary As Double  
  2. Public Property Salary() As Double  
  3.     Get  
  4.         Return mSalary  
  5.     End Get  
  6.     Set(value As Double)  
  7.         mSalary = value  
  8.         'RaiseEvent DataChanged("Salary", value) '出错,子类不能直接用语句引发基类事件  
  9.     End Set  
  10. End Property  


        但,可用Protected把该语句包装成方法后,供后面子类调用该方法,从而间接激发:

  1. '==========基类Person用Protected方法来包装RaiseEvent================  
  2.     Protected Sub OnDataChanged(ByVal field As String, ByVal value As Object)  
  3.         RaiseEvent DataChanged(field, value)  
  4.     End Sub  
  5.   
  6.   
  7. '==========子类Employee中用调用方法的形式间接激发事件==================  
  8.     Public Property Salary() As Double  
  9.         Get  
  10.             Return mSalary  
  11.         End Get  
  12.         Set(value As Double)  
  13.             mSalary = value  
  14.             'RaiseEvent DataChanged("Salary", value) '子类不能直接用语句引发基类事件  
  15.             OnDataChanged("Salary", value)  
  16.         End Set  
  17.     End Property  


         这样,在子程序分别定义好接收事件,并关联好事件:

  1. Public Class Form1  
  2.   
  3.     Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click  
  4.         Dim temp As Employee = New officeEmployee("zheng")  
  5.   
  6.         AddHandler temp.NameChanged, AddressOf OnNameChanged  
  7.         AddHandler temp.DataChanged, AddressOf OnDataChanged '关联到事件  
  8.         temp.Name = "Fred"  
  9.         temp.Salary = 300  
  10.     End Sub  
  11.   
  12.     Private Sub OnNameChanged(ByVal newName As String)  
  13.         MsgBox("Name Changed:" & newName)  
  14.     End Sub  
  15.   
  16.     Protected Sub OnDataChanged(ByVal field As String, ByVal newValue As Object) '事件处理  
  17.         MsgBox("New  " & field & ": " & CStr(newValue))  
  18.     End Sub  
  19.   
  20. End Class  
           注意的是主程序中接收事件OnDataChanged与类中的OnDateChanged是不一样的。

            因为类中的是Protected它只能在类内使用,不能对外。









四、共享方法


        共享方法是类在加载时就被加载到内存中的方法,在整个运行过程中保持不变,因而不能重写。

        但非共享方法是在对象实例化时才单独申请内存空间,为每一个实例分配独立的运行内存,因而可以重写。


        同时注意到共享方法是类加载(而不是对象实例化)时就产生,且固定了内存位置,而非共享方法是对象实例化时再

       分配内存空间,其内存的地址是随即产生,无法定向,所以共享方法不能访问非共享方法。按照C++的说法,就是共享

        方法是没有This指针的。


        共享方法可以被继承,也可以被重载或隐藏,但不能重写!!

        下例用类名调用共享比较方法:

  1. '==============Person类中的共享方法,添加代码=====================  
  2.     Public Shared Function Compare(ByVal person1 As Person, ByVal person2 As Person) As Boolean  
  3.         Return person1.Name = person2.Name  
  4.     End Function  
  5.   
  6. '==============主程序中调用程序===================  
  7.     Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click  
  8.         Dim emp1 As New Employee("Fred")  
  9.         Dim emp2 As New Employee("Mary")  
  10.         MsgBox(Person.Compare(emp1, emp2)) '继承共享方法  
  11.     End Sub  



         继承与重载共享方法

         继续添加,以便共享方法在子类Employee中重载(类型不一样):

  1. '==============Employee类中重载共享方法=====================  
  2.     Public Overloads Shared Function Compare(ByVal employee1 As Employee, ByVal employee2 As Employee) As Boolean  
  3.         Return employee1.EmployeeNumber = employee2.EmployeeNumber  
  4.     End Function  
  5.   
  6. '==============主程序中调用程序==========  
  7.     Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click  
  8.         Dim emp1 As New Employee("Fred")  
  9.         Dim emp2 As New Employee("Mary")  
  10.         emp1.EmployeeNumber = 1  
  11.         emp2.EmployeeNumber = 1  
  12.         MsgBox(Person.Compare(emp1, emp2)) '继承共享方法  
  13.         MsgBox(Employee.Compare(emp1, emp2)) '重载比较  
  14.     End Sub  

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

         如果上面重载方法移动至OfficeEmploy中时,显示结果将是:False,False

         因为它们两个都会调用Person中的Compare方法,所以结果一样。




        隐藏共享方法

       虽然不能重写,但可隐藏,在OfficeEmployee中定义一个与父类Employee方法签名一样的共享方法

  1. '==============OfficeEmploy类中隐藏共享方法=====================  
  2.     Public Shared Shadows Function Compare(ByVal person1 As Person, ByVal person2 As Person) As Boolean  
  3.         Return person1.Age = person2.Age  
  4.     End Function  
  5.   
  6. '==============主程序中调用程序==========  
  7.     Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click  
  8.         Dim emp1 As New Employee("Fred")  
  9.         Dim emp2 As New Employee("Mary")  
  10.         emp1.Age = 25  
  11.         emp2.Age = 20  
  12.         MsgBox(officeEmployee.Compare(emp1, emp2))  
  13.     End Sub  
        结果:显示False

       尽管对象是Employee,例共享方法是通过类名调用,这里用的是OfficeEmployee,故用它的共享方法。

        虽然它也继承了上一级Employee的共享方法,但因隐藏原因,将使用本类的共享方法。








五、共享事件


       1、共享事件可以被继承。象前面事件一样。

       2、同理子类中不能用RaiseEvent来引发父类的共享事件。只能象前面一样用Protected包装后,调用方法间接引发。











    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多