子类与父类之间的方法重载、隐藏、重写与虚方法调用

由于子类对象同时“汇集了”父类和子类的所有公共方法,如果子类中某个方法与父类方法的签名一样(即方法名和方法参数都一样),那当通过子类对象访问此方法时,访问的是子类还是父类所定义的方法?
总的来说,子类方法与父类方法之间的关系可以概括为以下三种。

    扩充(Extend):子类方法,父类没有;

    重载(Overload):子类有父类的同名函数,但参数类型或数目不一样;

    完全相同:子类方法与父类方法从方法名称到参数类型完全一样。

对于第一种“扩充”关系,由于子类与父类方法不同名,所以不存在同名方法调用的问题,重点分析一下后两种情况.
一、重载
和普通的方法重载一样,在同一个类中构成重载的方法主要根据参数列表来决定调用哪一个。
class Parent
{
  public void OverloadF()
  {
  }
}
class Child:Parent
{
  public void OverloadF(int i)
  {
  }
}
使用代码如下:
Child obj = new Child();
obj.OverloadF(); //调用父类的重载方法
obj.OverloadF(100);//调用子类的重载方法

可以看到,虽然重载的方法分布在不同的类中,但仍然可以将其看成是定义在同一个类中的,其使用方式与调用类的其他方法并无不同。


二、隐藏(Hide)
class Parent
{
  public void HideF()
  {
    System.Console.WriteLine("Parent.HideF()");
  }
}
class Child : Parent
{
  public void HideF()
  {
    System.Console.WriteLine("Child. HideF()"); //当子类与父类拥有完全一样的方法时,称“子类隐藏了父类的同名方法”
  }
}
注:Visual Studio 在编译这两个类时,会发出一个警告: “HideExamples.Child.HideF()”隐藏了继承的成员“HideExamples.Parent.HideF()”。如果是有意隐藏,请使用关键 new。


Child c = new Child();
c.HideF();//调用父类的还是子类的同名方法?
上述代码运行时,输出:
Child.HideF()
修改一下代码:
Parent p = new Parent();
p.HideF();//调用父类的还是子类的同名方法?
上述代码运行结果:
Parent.HideF()
由此可以得出一个结论:
当分别位于父类和子类的两个方法完全一样时,调用哪个方法由对象变量的类型决定。
然而,面向对象的继承特性允许子类对象被当成父类对象使用,这使问题复杂化了,请读者看以下代码,想想会出现什么结果?
Child c = new Child();
Parent p;
p = c;
p.HideF();//调用父类的还是子类的同名方法?
上述代码的运行结果是:
Parent.HideF()
这就意味着即使Parent 变量p 中实际引用的是Child 类型的对象,通过p 调用的方法还是Parent 类的!
如果确实希望调用的子类的方法,应这样使用:
((Child)p).HideF();
即先进行强制类型转换。
由于子类隐藏了父类的同名方法,如果不进行强制转换,就无法通过父类变量直接调用子类的同名方法,哪怕父类变量引用的是子类对象。
虽然上述警告并不影响程序运行结果,却告诉我们代码不符合C#的语法规范,修改Child 类的定义如下:
class Child : Parent
{
public new void HideF()
{
System.Console.WriteLine("Child.HideF()");
}
}
“new”关键字明确告诉C#编译器,子类隐藏父类的同名方法,提供自己的新版本。
由于子类隐藏了父类的同名方法,所以如果要在子类方法的实现代码中调用父类被隐藏的同名方法,请使用base 关键字,示例代码如下:

base.HideF(); //调用父类被隐藏的方法


三、重写(override)与虚方法调用
我们希望每个对象都只干自己职责之内的事,即如果父类变量引用的是子类对象,则调用的就是子类定义的方法,而如果父类变量引用的就是父类对象,则调用的是父类定义的方法。
为达到这个目的,可以在父类同名方法前加关键字virtual,表明这是一个虚方法,子类可以重写此方法:即在子类同名方法前加关键字override,表明对父类同名方法进行了重写。
class Parent
{
  public virtual void OverrideF()
  {
    System.Console.WriteLine("Parent.OverrideF()");
  }
}
class Child : Parent
{
  public override void OverrideF()
  {
    System.Console.WriteLine("Child.OverrideF()");
  }
}
请看以下使用代码:
Child c = new Child();
Parent p;
p = c;
p.OverrideF();//调用父类的还是子类的同名方法?
上述代码的运行结果是:
Child.OverrideF()
这一示例表明,将父类方法定义为虚方法,子类重写同名方法之后,通过父类变量调用此方法,到底是调用父类还是子类的,由父类变量引用的真实对象类型决定,而与父类变量无关!
换句话说,同样一句代码:
p.OverrideF();
在p 引用不同对象时,其运行的结果可能完全不一样!因此,如果我们在编程时只针对父类变量提供的对外接口编程,就使我们的代码成了“变色龙”,传给它不同的子类对象(这
些子类对象都重写了父类的同名方法),它就干不同的事。
这就是面向对象语言的“虚方法调用(Virtual Method Invoke)”特性。
很明显,“虚方法调用”特性可以让我们写出非常灵活的代码,大大减少由于系统功能扩充和改变所带来的大量代码修改工作量。
由此给出以下结论:
面向对象语言拥有的“虚方法调用”特性,使我们可以只用同样的一个语句,在运行时根据对象类型而执行不同的操作。
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值