在前面的文章中,我们介绍了编译期多态、params关键字、实例化、base关键字等。本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定。


运行时多态或迟绑定、动态绑定

在C#语音中,运行时多态也叫方法重写(overriding),我们可以在子类中overriding基类的同签名函数,使用“virtual & override”关键字即可。


C#的New、Override关键字

创建一个console 示例工程,命名为InheritanceAndPolymorphism。在Program.cs基础上,再添加2个类文件,分别命名为ClassA.cs、ClassB.cs。拷贝如下代码:

public class ClassA
{
public void AAA()
{
Console.WriteLine("ClassA AAA");
} public void BBB()
{
Console.WriteLine("ClassA BBB");
} public void CCC()
{
Console.WriteLine("ClassA CCC");
}
}

ClassB:

public class ClassB
{
public void AAA()
{
Console.WriteLine("ClassB AAA");
} public void BBB()
{
Console.WriteLine("ClassB BBB");
} public void CCC()
{
Console.WriteLine("ClassB CCC");
}
}

在上面的代码中,我们可以看到ClassA、ClassB有同样签名的方法,可以在program.cs中直接使用。

我们对代码再做休整,结构如下:

/// <summary>
/// ClassB, acting as a base class
/// </summary>
public class ClassB
{
public void AAA()
{
Console.WriteLine("ClassB AAA");
} public void BBB()
{
Console.WriteLine("ClassB BBB");
} public void CCC()
{
Console.WriteLine("ClassB CCC");
}
} /// <summary>
/// Class A, acting as a derived class
/// </summary>
public class ClassA : ClassB
{
public void AAA()
{
Console.WriteLine("ClassA AAA");
} public void BBB()
{
Console.WriteLine("ClassA BBB");
} public void CCC()
{
Console.WriteLine("ClassA CCC");
}
}

Program.cs

/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA x = new ClassA();
ClassB y=new ClassB();
ClassB z=new ClassA(); x.AAA(); x.BBB(); x.CCC();
y.AAA(); y.BBB();y.CCC();
z.AAA(); z.BBB(); z.CCC();
}
}

F5,运行代码,结果如下:

ClassA AAA

ClassA BBB

ClassA CCC

ClassB AAA

ClassB BBB

ClassB CCC

ClassB AAA

ClassB BBB

ClassB CCC

但同时,在VS的Output窗口,我们获得了3个Warnings:

'InheritanceAndPolymorphism.ClassA.AAA()' hides inherited member

'InheritanceAndPolymorphism.ClassB.AAA()'. Use the new keyword if hiding was intended.

'InheritanceAndPolymorphism.ClassA.BBB()' hides inherited member

'InheritanceAndPolymorphism.ClassB.BBB()'. Use the new keyword if hiding was intended.

'InheritanceAndPolymorphism.ClassA.CCC()' hides inherited member

'InheritanceAndPolymorphism.ClassB.CCC()'. Use the new keyword if hiding was intended.

这些Warnings的原因是因为子类和基类的AAA、BBB、CCC方法签名相同,尽管从执行上看优先执行子类同签名的方法,但是可能会有潜在的问题,故Warnings提出。


重构实验

基于上面的Warning,我们手动修改代码,看看如何消除这些Warnings。

先给子类添加new、override关键字试试:

/// <summary>
/// Class A, acting as a derived class
/// </summary>
public class ClassA : ClassB
{
public override void AAA()
{
Console.WriteLine("ClassA AAA");
} public new void BBB()
{
Console.WriteLine("ClassA BBB");
} public void CCC()
{
Console.WriteLine("ClassA CCC");
}
}

执行的结果是报错了:

Error: 'InheritanceAndPolymorphism.ClassA.AAA()': cannot override inherited member 'InheritanceAndPolymorphism.ClassB.AAA()' because it is not marked virtual, abstract, or override

从这个错误提示信息看,我们需要修改基类方法,如添加virtual关键字。

/// <summary>
/// ClassB, acting as a base class
/// </summary>
public class ClassB
{
public virtual void AAA()
{
Console.WriteLine("ClassB AAA");
} public virtual void BBB()
{
Console.WriteLine("ClassB BBB");
} public virtual void CCC()
{
Console.WriteLine("ClassB CCC");
}
} /// <summary>
/// Class A, acting as a derived class
/// </summary>
public class ClassA : ClassB
{
public override void AAA()
{
Console.WriteLine("ClassA AAA");
} public new void BBB()
{
Console.WriteLine("ClassA BBB");
} public void CCC()
{
Console.WriteLine("ClassA CCC");
}
} /// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB y = new ClassB();
ClassA x = new ClassA();
ClassB z = new ClassA(); y.AAA(); y.BBB(); y.CCC();
x.AAA(); x.BBB(); x.CCC();
z.AAA(); z.BBB(); z.CCC(); Console.ReadKey();
}
}

执行,则无Warning了,通过这个实例,我们得知通过在基类添加Virtual关键字授权其子类可override基类同签名方法的权限,方便了OOP的扩展。


3个类的运行时多态

/// <summary>
/// ClassB, acting as a base class
/// </summary>
public class ClassB
{
public void AAA()
{
Console.WriteLine("ClassB AAA");
} public virtual void BBB()
{
Console.WriteLine("ClassB BBB");
} public virtual void CCC()
{
Console.WriteLine("ClassB CCC");
}
} /// <summary>
/// Class A, acting as a derived class
/// </summary>
public class ClassA : ClassB
{
public virtual void AAA()
{
Console.WriteLine("ClassA AAA");
} public new void BBB()
{
Console.WriteLine("ClassA BBB");
} public override void CCC()
{
Console.WriteLine("ClassA CCC");
}
} /// <summary>
/// Class C, acting as a derived class
/// </summary>
public class ClassC : ClassA
{
public override void AAA()
{
Console.WriteLine("ClassC AAA");
} public void CCC()
{
Console.WriteLine("ClassC CCC");
}
} /// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB y = new ClassA();
ClassB x = new ClassC();
ClassA z = new ClassC(); y.AAA(); y.BBB(); y.CCC();
x.AAA(); x.BBB(); x.CCC();
z.AAA(); z.BBB(); z.CCC(); Console.ReadKey();
}
}

运行结果:

ClassB AAA

ClassB BBB

ClassA CCC

ClassB AAA

ClassB BBB

ClassA CCC

ClassC AAA

ClassA BBB

ClassA CCC

如果基类声明了virtual 关键字,子类可使用override修饰符实现运行时多态:只有在编译器动态决定是否被调用。

如果未标明virtual或非virtual,则方法是否被调用在编译期就能决定。

再看看下面的例子:

internal class A
{
public virtual void X()
{
}
} internal class B : A
{
public new void X()
{
}
} internal class C : B
{
public override void X()
{
}
}

F5运行,结果报错了:

Error: 'InheritanceAndPolymorphism.C.X()': cannot override inherited member 'InheritanceAndPolymorphism.B.X()' because it is not marked virtual, abstract, or override

错误的原因是A中定义了virtual的X函数,在B中用new关键字隐藏了A中的X函数。当C尝试通过override关键字的时候,是获得不了A中的virtual关键字X函数的,既在C中X函数为非Virtual的,故不能override。


切断关系

internal class A
{
public virtual void X()
{
Console.WriteLine("Class: A ; Method X");
}
} internal class B : A
{
public new virtual void X()
{
Console.WriteLine("Class: B ; Method X");
}
} internal class C : B
{
public override void X()
{
Console.WriteLine("Class: C ; Method X");
}
} /// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
A a = new C();
a.X();
B b = new C();
b.X(); Console.ReadKey();
}
}

执行结果如下:

Class: A ; Method X
Class: C ; Method X

在这里,我们通过在B类中添加new Virtual修饰符,然后在C中即可使用B中Virtual的X函数了。


4个类的运行时多态

在上面继承上,在运行时多态中添加第四个类:ClassD。

/// <summary>
/// Class A
/// </summary>
public class ClassA
{
public virtual void XXX()
{
Console.WriteLine("ClassA XXX");
}
} /// <summary>
/// ClassB
/// </summary>
public class ClassB:ClassA
{
public override void XXX()
{
Console.WriteLine("ClassB XXX");
}
} /// <summary>
/// Class C
/// </summary>
public class ClassC : ClassB
{
public virtual new void XXX()
{
Console.WriteLine("ClassC XXX");
}
} /// <summary>
/// Class D
/// </summary>
public class ClassD : ClassC
{
public override void XXX()
{
Console.WriteLine("ClassD XXX");
}
} /// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA a = new ClassD();
ClassB b = new ClassD();
ClassC c=new ClassD();
ClassD d=new ClassD(); a.XXX();
b.XXX();
c.XXX();
d.XXX(); Console.ReadKey();
}
}

执行结果如下:

ClassB XXX

ClassB XXX

ClassD XXX

ClassD XXX

第一行输出中,来自a.XXX()函数 , 我们在 ClassA中定义了XXX函数,然后在ClassB中使用new关键字切断了virtual关系--对子类而言。因此XXX函数从ClassC开始成为新的virtual函数,在这个代码中a是ClassD的实例,但是声明的为ClassA,故从下往上找,找到ClassB的XXX函数,打印并输出结果。


永无止境的循环

/// <summary>
/// Class A
/// </summary>
public class ClassA
{
public virtual void XXX()
{
Console.WriteLine("ClassA XXX");
}
} /// <summary>
/// ClassB
/// </summary>
public class ClassB:ClassA
{
public override void XXX()
{
((ClassA)this).XXX();
Console.WriteLine("ClassB XXX");
}
} /// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA a = new ClassB();
a.XXX(); }
}

运行报错:

Error: {Cannot evaluate expression because the current thread is in a stack overflow state.}

在这个例子中,((ClassA)this).XXX(); 导致了循环调用,修改为base.XXX即可修复这个强转导致的循环调用。


结论

  • 在C#中,子类对象可赋值给一个基类对象;相反需要强转。

  • override关键字用于子类重写同签名的基类virtual函数

  • 用new和override可重写基类virtual的同签名函数

  • virtual修饰符的函数,只能在运行时决定是否被执行

  • 函数未用virtual修饰,则在编译期即可决定是否被调用

译文链接:http://www.cnblogs.com/powertoolsteam/p/Diving-in-OOP-Day-Polymorphism-and-Inheritance-Dyn.html

原文链接:Diving in OOP (Day 3): Polymorphism and Inheritance (Dynamic Binding/Run Time Polymorphism)

深入理解OOP(三):多态和继承(动态绑定和运行时多态)的更多相关文章

  1. 深入浅出OOP(三): 多态和继承(动态绑定/运行时多态)

    在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 运行时多态或迟绑定.动态绑定 在C#语音中,运行时 ...

  2. Java编译时多态和运行时多态

    来源:https://blog.csdn.net/wendizhou/article/details/73733061 编译时多态:主要是方法的重载,通过参数列表的不同来区分不同的方法. 运行时多态: ...

  3. 深入理解OOP(四): 多态和继承(抽象类)

    在本文中,我们讨论OOP中的热点之一:抽象类.抽象类在各个编程语言中概念是一致的,但是C#稍微有些不一样.本文中我们会通过代码来实现抽象类,并一一进行解析. 深入理解OOP(一):多态和继承(初期绑定 ...

  4. 深入理解OOP(二):多态和继承(继承)

    本文是深入浅出OOP第二篇,主要说说继承的话题. 深入理解OOP(一):多态和继承(初期绑定和编译时多态) 深入理解OOP(二):多态和继承(继承) 深入理解OOP(三):多态和继承(动态绑定和运行时 ...

  5. 深入理解OOP(第一天):多态和继承(初期绑定和编译时多态)

    在本系列中,我们以CodeProject上比较火的OOP系列博客为主,进行OOP深入浅出展现. 无论作为软件设计的高手.或者菜鸟,对于架构设计而言,均需要多次重构.取舍,以有利于整个软件项目的健康构建 ...

  6. PHP面向对象三大特点学习(充分理解抽象、封装、继承、多态)

    PHP面向对象三大特点学习 学习目标:充分理解抽象.封装.继承.多态   面象对向的三大特点:封装性.继承性.多态性 首先简单理解一下抽象:我们在前面定义一个类的时候,实际上就是把一类事物共有的属性和 ...

  7. OOP面向对象 三大特征 继承封装多态

    OOP面向对象 ----三大特征 继承封装多态 面向对象(Object Oriented,OO)是软件开发方法.面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统.交互式界面.应用结构 ...

  8. Java中的继承、封装、多态的理解

    Java中的继承.封装.多态 继承的理解: 1.继承是面向对象的三大特征之一,也是实现代码复用的重要手段.Java的继承具有单继承的特点,每个子类只有一个直接父类. 2.Java的继承通过extend ...

  9. OOP、封装、继承、多态,真的懂了吗?

    平时只要一提起来面向对象编程OOP的好处,随口就能说出来,不就是封装.继承.多态么,可他们的含义是什么呢,怎么体现,又有什么非用不可的好处啊.可能平时工作中天天在用OOP,仅仅是在用OOP语言,就是一 ...

随机推荐

  1. linux性能监控工具

    1.uptime 该命令直观的显示了服务器在过去15分钟,5分钟,1分钟内的平均负载   2.vmstat 每隔2秒输出vmstat的信息,共输出10次. 类别 procs swap io   sys ...

  2. WebApi深入学习--概述+路由查找

    如何创建Controller这里就不说了,只写一些可能是高阶知识的内容 关于WebApi的官方介绍及示例 http://www.asp.net/web-api/ 1.跨域 Asp.NET有专门的跨域扩 ...

  3. Mysql 创建用户 授权

    一, 创建用户: 命令:CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 说明:username - 你将创建的用户名, host - 指 ...

  4. x01.os.1: BIOS 中断

    这只是一点准备工作.为了显示字符串,需要调用中断:int  0x10 (AH=0x13).具体参数设置,参考我的归纳整理如下: INT 10 (AH = 0) -----------------功能: ...

  5. cookie和session详解

    cookie和session的区别 二者的定义: 当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cookie 会帮你在网站上所打的文字或是一些选择,都纪录下来.当下次你再光临同 ...

  6. linux 下 mysql 安装(不用编译的方式)

    环境是centos6.x.相信其他的也大同小异.相对来说这种方式我用的比较多. 一些环境依赖表库: yum install perl yum install libaio 1)下载:在mysql的网站 ...

  7. vsftpd 配置:chroot_local_user与chroot_list_enable详解

    chroot_local_user,chroot_list_enable,chroot_list_file三个配置项的解释: chroot_local_user #是否将所有用户限制在主目录,YES为 ...

  8. 开发Eclipse自定义控件

    摘自:http://www.ibm.com/developerworks/cn/opensource/os-eclipcntl/ 我们在开发自定义控件时主要考虑以下问题: 1. 自定义控件的绘制:通常 ...

  9. MMORPG大型游戏设计与开发(客户端架构 part6 of vegine)

    客户端的变量模块部分主要是将一些常用可变的值集中管理,如窗口的大小,是否开启音乐,音量的大小等等.这些变量通常会应该到客户端的操作,一般来说变量改变的时候会调用一个回调进行处理.下面我们就看看该模块的 ...

  10. JAVA 字符串驻留池

    一切从String str = new String("abc")说起...    这行代码形式上很简单,其实很复杂.有一个常见的Java笔试题就是问上面这行代码创建了几个Stri ...