类 的重载(Overloads)与隐藏(Shadows)

       我在上篇文章中讲解了类 的继承和重写,如果想要在派生类中重写基类了方法或函数,那首先基类必须要有用 Overridable 关键字的公开声明的方法或函数,这样,基类的派生类才能用 Overrides 关键字来重写基类带有 Overridable 关键字的公开方法或函数。

      重载是什么意思呢?

      重载(Overloads): 就是我们可以用同样的名称,在派生类中用不同的参数列表来创建多个方法和属性,在调用时就可以适应不同参数类型的要求。

       呵呵...那好,我们来看一下,这样的写法会有什么结果:

Module Module1
    Public Class baseClass
        Public Function CountY() As Integer
            CountY = 100
        End Function
    End Class

    Private Class derivedClass
        Inherits baseClass
        Public Overloads Function CountY(ByVal i As Integer) As Integer
            CountY = i * 2
        End Function
    End Class

End Module

        好!基类和派生类写好了,派生类中也重载了基类的 ConutY 函数,那我们就来实例化派生类,并使用它的函数看看什么情况:

1

1

       呵呵,看到了吧,居然有两个 CountY 函数,一个无参数的和一个有参数的!!为什么会这样呢?基类的无参数 CountY 函数怎么会出现在派生类的对象中?

         没错 ,我在类的继承和重写的文章中讲到,派生类具有继承基类所以公共成员的能力,你再看基类的 CountY 函数,发现了吧,它用的是Public 关键字声明的,这是公开性的函数。所以...

        另外:还记得上篇文章也介绍“显式编写构造函数 New 的事” ,我用了一个 mybase.new() 继承了基类的性质。现在没有写,其实是一种默认的隐式调用。

        所以现在的派生类应该是内含两个 CountY 函数了,无参数的 CountY 其实就是基类的方法,是由于 Overloads 和 Mybase.new() 双重作用的影响。

        那再看这样的变化情况。我们在派生类中添加一个用 Overloads 标识的与基类形式一致的无参数 CountY 后,再执行会出现什么情况:

Module Module1
    Public Class baseClass
        Public Function CountY() As Integer
            CountY = 100
        End Function
    End Class

    Private Class derivedClass
        Inherits baseClass
        Public Overloads Function CountY(ByVal i As Integer) As Integer
            CountY = i * 2
        End Function
        Public Overloads Function CountY() As Integer
            CountY = 200
        End Function
    End Class
    Sub main()
        Dim obj As New derivedClass
        Console.WriteLine("带参数的 CountY 函数的返回值为:{0} ", obj.CountY(10))
        Console.WriteLine("无参数的 CountY 函数的返回值为:{0} ", obj.CountY())
        Console.Read()
    End Sub

End Module

        我们来看看结果:

1

怎么样,是不是很好奇,为什么是执行的派生类中的无参数函数 CountY ,而不是执行的基类中的无参数函数 CountY ?

其实这也可以叫隐藏(Shadows)!

我们用 Overloads 重载方式隐藏了基类的同名方法,以防用户发生混淆。

Shadows功能很强的。

Private Class derivedClass
        Inherits baseClass
        Public Shadows Function CountY(ByVal i As Integer) As Integer
            CountY = i * 2
        End Function
End Class

      我们用Shadows 关键字代替Overloads 来实现重载后,看看会出现几个 CountY 函数:

image

      看到了吧,只有一个了!我们看运行结果:

Sub main()
    Dim obj As New derivedClass
    Console.WriteLine("带参数的 CountY 函数的返回值为:{0}", obj.CountY(50))
End Sub

      如图:

1

      哈,基类的方法看不到了。但是,这和重写的效果岂不是一样了吗?

      如果是一样的作用,要 Shadows 干什么呀。

      其实还是有区别的。最明显的区别在于,隐藏适用于任何元素类型,你可以在派生类中用:

         Public Shadows CountY as Integer

     来隐藏基类的 CountY() 方法;而重写只能适用于方法与属性,而且声明参数与修饰符都要求完全一致。

      还要注意一点,当派生类又派生出一个子类时,重写与隐藏都会被继承下去。当然,如果在派生类中是用 private 来修饰 Shadows 成员的话,它的子类就会继承它基类的成员了。

 

隐藏(Shadows)

      一般来说,隐藏有两种情况:一种是通过范围来实现。比如你定义一个全局变量 x,但在一个方法中 ,你又定义了一个局部变量 x,在方法中使用 x 时,使用的是局部变量的那一个, 也就是局部变量 x 在方法中隐藏了全局变量x(这里牵涉到作用域的概念,所以这个比如不是很恰当);另一种情况,就是通过继承来隐藏。方法么,除了刚才用的 overloads,还可以用 Shadows 关键字来实现。Shadows(遮蔽、阴影),倒是很贴切的名字。

        隐藏就如同作用域一样,全局变量在过程中被局部同名变量隐藏。

       如果基类设计有误而又无法得到源码,或者基类适用大多情况,但有特殊情况时又得改写。由于基类中方法设计时就是不允许重写(没有 Overridable ),这里想在子类中“改写”这个方法,怎么办?当然是用 Shadows 来隐藏基类的同名方法。

         简单地说:

       重写是征得(Overridable)允许后的改写;而隐藏则是未经允许就强行改写基类的方法。

        当声明一个方法,如果不是用 Overrides 关键字,它就是非虚拟方法,而非虚拟方法是不能被子类重写和替代的方法。

        而隐藏就是这样,它要重写非虚拟方法,不管它们是否声明时使用了 Overridable ,它无视规则。

         所以,隐藏比重写更血腥,如果说重写是有法有依的司法人员,那么隐藏就是无法无天的抢劫犯。

         因此隐藏打破了规则,基类开发人员在把方法标记或不标记 Overridable 时总是很小心,确保方法在子类是否重写,以

         保证基类能够继续正常使用。常常没有标记 Overridable 的不希望重写,而 Shadows 却打破了这个规则。

          然而,使用Shadows根本上改变了Employee的功能,便它不再是Person对象,这种本质上背离正常期望的方法会

          引发错误,并使得代码难于理解和维护。

          所以隐藏方法是一个危险的方法!!!

          看例子,先在Person中定义非虚拟方法---只读Age,使其故意为负,以便在子类中改正:

Module Module1
    Public Class basePerson
        Public Overridable Property BirthDate() As Date
        Public Overridable Property Name() As String
        Public ReadOnly Property Age() As Integer
            Get
                      '正常的第二个参数为给定日期,第三个参数为参照对比的日期,顺序反了会是负数
                Return CInt(DateDiff(DateInterval.Year, Now, BirthDate))  '因参数位置错误,导致结果为负数
            End Get
        End Property
    End Class
  
  Public Class DerivedClass
        Inherits basePerson
       
    End Class
    Sub main()
        Dim obj As New DerivedClass
        Dim a As Integer = obj.Age   '所以这里的Age属性是基类的。
        Console.WriteLine("我们来看看,Age 属性的值:{0}", a)
        Console.Read()
    End Sub

End Module

      如图:

1

      那我们在子类 DerivedClass 中来隐藏基类的错误算法,写个正确的来代替:

Module Module1
    Public Class basePerson
        Public Overridable Property BirthDate() As Date
        Public Overridable Property Name() As String
        Public ReadOnly Property Age() As Integer
           Get
               '正常的第二个参数为给定日期,第三个参数为参照对比的日期,顺序反了会是负数
           Return CInt(DateDiff(DateInterval.Year, Now, BirthDate))  '因参数位置错误,导致结果为负数
        End Get
        End Property
    End Class
    Public Class DerivedClass
     Inherits basePerson
        'Shadows可省略,但会出现警告,只要函数名称相同,就会隐藏基类的方法或属性 
      Public Shadows ReadOnly Property Age() As Integer 
            Get
              Return DateDiff(DateInterval.Year, BirthDate, Now)  '本来此属性是不可重写与重装的,但加了 Shadows 隐藏关键字,所以可被强制改写!!  
         End Get
        End Property
    End Class
    Sub main()
        Dim obj As New DerivedClass
        Dim a As Integer = obj.Age   '所以这里的Age属性就不是基类的,而是派生类的。
        Console.WriteLine("带参数的 CountY 函数的返回值为:{0}", a)
        Console.Read()
    End Sub

End Module

        注意:Shadows 是可省略的,但IDE会警告,最好加上。隐藏发生在方法签名相同的情况中。

      错误警告如图:

1

        注意:这里派生类的 Age 属性,并不是重写基类的 Age 属性。属性没有重写一这说法!只有方法才能重写 。

        如果派生类中有了和基类中某个属性的名称相同的属性,这时,派生类就是隐藏了基类的属性,只让派生类中同名的属性有效。 

       我们来看下结果:

1

      结果为正数的年份,所以,调用的是派生类 DerivedClass 的 Age 属性,因为此属性用 Shadows 关键字隐藏了基类的错误属性。

     Shadows 是一个怪兽,它可以改变原来的只读为可读写:

Module Module1
    Public Class basePerson
        Public Overridable Property BirthDate() As Date
        Public Overridable Property Name() As String
        Public ReadOnly Property Age() As Integer
            Get
                '正常的第二个参数为给定日期,第三个参数为参照对比的日期,顺序反了会是负数
                Return CInt(DateDiff(DateInterval.Year, Now, BirthDate))  '因参数位置错误,导致结果为负数
            End Get
        End Property
    End Class
   Public Class DerivedClass
        Inherits basePerson      '本来此属性是不可重写的,但加了 Shadows 隐藏关键字,所以可被强制重写!! 
        Public Shadows Property Age() As Integer  '将基类的只读属性,改为可读写属性。
            Get
                Return DateDiff(DateInterval.Year, BirthDate, Now)  '根据出生年份和现在的年份来计算间隔差的值,得到年龄。 
            End Get
            Set(value As Integer)
                BirthDate = DateAdd(DateInterval.Year, -value, Now) '现在的日期和你给定的年龄进行负运行,就能得到你的出生年份
            End Set
        End Property

    End Class
End Module

        可以看去有 Shadows 在,给只读的属性增加可写的功能也能实现…

       但这样使得使用时会发生意想不到的问题,下面变量改为类型 BaseClass,所以 Age 只能为只读,所以提示出错:

1

        如果更为极端时,Shadows这个怪兽还可以把方法变成实例变量、或将属性变成方法。

        比如在子类 DerivedClass 中把 Age 变成下面:

         Public  Shadows  Age  As String

        这都说明了 Shadows 在隐藏基类元素时,子类会改变其牲或作用域。

         说到作用域,隐藏实际上就是作用域的情况,局部变量隐藏全局变量,使得全局变量在过程中失效一样:

新建位图图像

       现在 VB.NET 对继承的处理功能真的很强大,继承层次体系是有很多好处,但是事物总是有其双面性 ,继承也有不少问题,其中最麻烦的,就是 “ 基类的脆弱之处 ”。

下篇文章将继续解说....

posted on 2015-11-17 17:49  I_am  阅读(1374)  评论(0编辑  收藏  举报

导航