我在上篇文章中讲解了类 的继承和重写,如果想要在派生类中重写基类了方法或函数,那首先基类必须要有用 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 函数,那我们就来实例化派生类,并使用它的函数看看什么情况:

呵呵,看到了吧,居然有两个 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

我们来看看结果:

怎么样,是不是很好奇,为什么是执行的派生类中的无参数函数 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 函数:

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

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,使其故意为负,以便在子类中改正:

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

如图:

那我们在子类 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会警告,最好加上。隐藏发生在方法签名相同的情况中。

错误警告如图:

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

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

我们来看下结果:

结果为正数的年份,所以,调用的是派生类 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 只能为只读,所以提示出错:

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

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

Public  Shadows  Age  As String

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

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

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

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

类 的重载(Overloads)与隐藏(Shadows)的更多相关文章

  1. C++ 派生类覆盖重载基类函数

    派生类希望基类重载函数可见,情况有三种: a)派生类中覆盖某个版本,则某个版本可见,全部都覆盖重写,则全部版本可见. b)派生类中一个也不覆盖,则全部基类版本可见. c)派生类需要添加新的重载版本,同 ...

  2. 自绘CListCtrl类,重载虚函数DrawItem

    //自绘CListCtrl类,重载虚函数DrawItem void CNewListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // TOD ...

  3. delphi 中record 的类操作符重载简介

    今天简单介绍一下 delphi 中record 的类操作符重载使用,就是如何 实现 record 之间的简单操作. 关于类操作符重载 ,大家可以看官方的文档. Delphi allows certai ...

  4. C++学习笔记23,类内函数重载

    该博文仅用于交流学习.请慎用于不论什么商业用途,本博主保留对该博文的一切权利. 博主博客:http://blog.csdn.net/qq844352155 转载请注明出处: 在一个类内,最常见的就是构 ...

  5. makefile中重载与取消隐藏规则示例

    学习<跟我一起写Makefile-陈皓>后一直不懂,如何重载或取消隐藏规则 为了博客版面整洁,何为隐藏规则,Makefile基本规则编写等基础支持请自行百度. 需要声明的是:这些知识可能在 ...

  6. oop_day02_类、重载_20150810

    oop_day02_类.重载_20150810 1.怎样创建类?怎样创建对象? 2.引用类型之间画等号: 家门钥匙 1)指向同一个对象(数据有一份) 2)对当中一个引用的改动.会影响另外一个引用 基本 ...

  7. c++类运算符重载遇到的函数形参问题

    class A { public: A(int arg1, int arg2); ~A(); A &operator = ( A &other); A operator + ( A & ...

  8. C++走向远洋——55(项目一3、分数类的重载、>>

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  9. C++走向远洋——54(项目一2、分数类的重载、取倒数)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

随机推荐

  1. li浮动引起ul高度坍陷的解决方法

    我们都知道float在CSS中的作用是使元素脱离正常的文档流并使其移动到其父元素的“最左边”或“最右边”.元素浮动之后,它脱离当前正常的文档流,所以无法撑开其父元素,造成父元素的高度塌陷.如下的代码就 ...

  2. HTML5_1

    2014,新年伊始,我不再是那个憧憬离开大学校园.过自由生活的傻丫头了.23岁,时间荏苒,差不多四分之一的人生已悄然逝去,大学生活差不多也快要画上句号了.工作.工作永远都是人生的一部分.曾想着随便找一 ...

  3. ububru下 redmine安装教程

    1.安装之前确认你已经安装好了mysql 2.连接mysql创建数据库 mysql -u root –p CREATE DATABASE redmine CHARACTER SET utf8; CRE ...

  4. tuple只有一个元素的时候,必须要加逗号

    In [1]: a = (1) In [2]: a Out[2]: 1 In [3]: a = (1,) In [4]: a Out[4]: (1,) 这是因为括号()既可以表示tuple,又可以表示 ...

  5. 重读LPTHW-Lesson1-14

    1.python print 可以用"Hello World",也可以用'Hello World',或者”””Hello World ””” 2.要打印在一行,可以在改行后加英文逗 ...

  6. Django学习(七) 创建第一个Django项目

    如果这是你第一次使用Django,你必须进行一些初始设置.即,您将需要自动生成一些代码,建立了Django项目. 从命令行.cd进入一个目录,你想要存储您的代码,然后运行以下命令: django-ad ...

  7. nginx中配置跨域支持功能

    vi /etc/nginx/nginx.conf 加入如下代码 http {  ###start####  add_header Access-Control-Allow-Origin *;  add ...

  8. SRAM与SDRAM的比较(转)

    原文:http://blog.csdn.net/fg8181/article/details/2278100 内存在电脑中起着举足轻重的作用,一般采用半导体存储单元,包括随机存储器(RAM),只读存储 ...

  9. 十一、外观(Facade)模式--结构模式(Structural Pattern)

    外部与一个子系统的通信必须通过一个统一的门面(Facade)对象进行,这就是门面模式.门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的门面(Facade)对象进行. 门面模式提供一个高层次 ...

  10. Luci流程分析(openwrt下)

    1. 页面请求: 1.1. 代码结构 在openwrt文件系统中,lua语言的代码不要编译,类似一种脚本语言被执行,还有一些uhttpd服务器的主目录,它们是: /www/index.html cgi ...