我在上篇文章中讲解了类 的继承和重写,如果想要在派生类中重写基类了方法或函数,那首先基类必须要有用 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 ClassEnd Module
好!基类和派生类写好了,派生类中也重载了基类的 ConutY 函数,那我们就来实例化派生类,并使用它的函数看看什么情况:
呵呵,看到了吧,居然有两个 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 SubEnd Module
我们来看看结果:
怎么样,是不是很好奇,为什么是执行的派生类中的无参数函数 CountY ,而不是执行的基类中的无参数函数 CountY ?
其实这也可以叫隐藏(Shadows)!
我们用 Overloads 重载方式隐藏了基类的同名方法,以防用户发生混淆。
Shadows功能很强的。
Private Class derivedClass Inherits baseClass Public Shadows Function CountY(ByVal i As Integer) As Integer CountY = i * 2 End FunctionEnd Class
我们用Shadows 关键字代替Overloads 来实现重载后,看看会出现几个 CountY 函数:
看到了吧,只有一个了!我们看运行结果:
Sub main() Dim obj As New derivedClass Console.WriteLine("带参数的 CountY 函数的返回值为:{0}", obj.CountY(50))End Sub
如图:
哈,基类的方法看不到了。但是,这和重写的效果岂不是一样了吗?
如果是一样的作用,要 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,使其故意为负,以便在子类中改正:
如图:
那我们在子类 DerivedClass 中来隐藏基类的错误算法,写个正确的来代替:
注意:Shadows 是可省略的,但IDE会警告,最好加上。隐藏发生在方法签名相同的情况中。
错误警告如图:
注意:这里派生类的 Age 属性,并不是重写基类的 Age 属性。属性没有重写一这说法!只有方法才能重写 。
如果派生类中有了和基类中某个属性的名称相同的属性,这时,派生类就是隐藏了基类的属性,只让派生类中同名的属性有效。
我们来看下结果:
结果为正数的年份,所以,调用的是派生类 DerivedClass 的 Age 属性,因为此属性用 Shadows 关键字隐藏了基类的错误属性。
Shadows 是一个怪兽,它可以改变原来的只读为可读写:
可以看去有 Shadows 在,给只读的属性增加可写的功能也能实现…
但这样使得使用时会发生意想不到的问题,下面变量改为类型 BaseClass,所以 Age 只能为只读,所以提示出错:
如果更为极端时,Shadows这个怪兽还可以把方法变成实例变量、或将属性变成方法。
比如在子类 DerivedClass 中把 Age 变成下面:
Public Shadows Age As String
这都说明了 Shadows 在隐藏基类元素时,子类会改变其牲或作用域。
说到作用域,隐藏实际上就是作用域的情况,局部变量隐藏全局变量,使得全局变量在过程中失效一样:
现在 VB.NET 对继承的处理功能真的很强大,继承层次体系是有很多好处,但是事物总是有其双面性 ,继承也有不少问题,其中最麻烦的,就是 “ 基类的脆弱之处 ”。
下篇文章将继续解说....