加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-17 08:09:12 所属栏目:百科 来源:网络整理
导读:例子源码:http://download.csdn.net/detail/dzweather/5916173 (复习备用) 一、构造函数 继承对构造函数方法的影响与对常规方法的影响不同。 1、不带参的构造函数 用New来创建构造函数。它于创建对象时才运行一次。 构造函数不会有Overridable,Overrides


例子源码: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 thatinherit 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,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,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包装后,调用方法间接引发。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读