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

时间:2022-12-20 17:23:49

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

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

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

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

我们来看看结果:

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

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

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

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

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

如图:

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

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

如果是一样的作用,要 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

如图:

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

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

错误警告如图:

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

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

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

我们来看下结果:

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

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

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

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

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

Public  Shadows  Age  As String

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

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

类 的重载(Overloads)与隐藏(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&lowbar;day02&lowbar;类、重载&lowbar;20150810

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

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

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

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

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

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

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

随机推荐

  1. Java连接Oracle数据库开发银行管理系统【二、设计篇】

    一.总体分析     此系统的实现并不难,但是如何更好的设计出实现方式还是需要更深入的分析,例如:如果再增加其他功能,是不是需要变动的 代码很少,只是直接再增加一点功能就可以了呢?如果使用的不是Ora ...

  2. --hdu 1231 最大连续子序列&lpar;动态规划&rpar;

    AC code: #include<stdio.h> int a[100005]; int main(void) { int n,i; int sum,maxn,tem,s,e,flag; ...

  3. 二维数组实现checkbox的分组多选

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <script language="j ...

  4. Codeforces Round &num;258 &lpar;Div&period; 2&rpar; B&period; Sort the Array

    题目链接:http://codeforces.com/contest/451/problem/B 思路:首先找下降段的个数,假设下降段是大于等于2的,那么就直接输出no,假设下降段的个数为1,那么就把 ...

  5. hdu&lowbar;1513&lowbar;Palindrome&lpar;LCS&plus;滚动数组&rpar;

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1513 题意:给你一个字符串,问你最少插入多少个字符使其为回文字符. 题解:将字符串倒着保存,然后求一下 ...

  6. Maven学习3(*仓库)

    Maven项目在运行的时候,会首先找本地仓库是否有需要的jar,如果没有则去调用远程仓库. 解读Maven在仓库中的存储路径: 1.基于groupId准备路径,将句点分隔符转成路径分隔符,就是将  & ...

  7. hadoop集群故障排除

    故障一:某个datanode节点无法启动 我是以用户名centos安装和搭建了一个测试用的hadoop集群环境,也配置好了有关的权限,所有者.所属组都配成centos:centos [故障现象] 名称 ...

  8. C&num; System&period;IO&period;File

    using System; using System.IO; class Test { public static void Main() { string path = @"c:\temp ...

  9. AngularJS 模块及provide

    一.模块 模块是一些功能的集合,如控制器.服务.过滤器.指令等子元素组成的整体. 1.注册和使用 模块相当于是一个注册表,保存着名字和编程元素的对照表,可存入也可取出. angular.module( ...

  10. 20155330 《网络对抗》 Exp8 Web基础

    20155330 <网络对抗> Exp8 Web基础 实验问题回答 什么是表单 表单可以收集用户的信息和反馈意见,是网站管理者与浏览者之间沟通的桥梁. 一个表单有三个基本组成部分 表单标签 ...