C# virtual和override
本文转载来自于:http://bollaxu.iteye.com/blog/1662855
在函数的声明中,当有“virtual”修饰的时候,和没有virtual有什么区别呢?最重要的一点就是调用实例的函数是在编译的时候确定还是在运行的时候确定,virtual函数是在运行的时候来确定具体调用哪个类。这个特性是和父子类继承息息相关的。
这儿有个例子,在网上很多地方被转载,我稍微扩展了一下:
using System;
namespace Smz.Test
{
class A
{
public virtual void Func() // 注意virtual,表明这是一个虚拟函数
{
Console.WriteLine("Func In A");
} public void Non_virtual()
{
Console.WriteLine("Non virtual func in A");
}
}
class B : A // 注意B是从A类继承,所以A是父类,B是子类
{
public override void Func() // 注意override ,表明重新实现了虚函数
{
Console.WriteLine("Func In B");
} public void Non_virtual()
{
Console.WriteLine("Non virtual func in B");
}
}
class C : B // 注意C是从A类继承,所以B是父类,C是子类
{
public void Non_virtual()
{
Console.WriteLine("Non virtual func in C");
}
}
class D : A // 注意D是从A类继承,所以A是父类,D是子类
{
public new void Func() // 注意new ,表明覆盖父类里的同名类,而不是重新实现
{
Console.WriteLine("Func In D");
} public new void Non_virtual()
{
Console.WriteLine("Non virtual func in D");
}
}
class program
{
static void Main()
{
A a; // 定义一个a这个A类的对象.这个A就是a的申明类
A b; // 定义一个b这个A类的对象.这个A就是b的申明类
A c; // 定义一个c这个A类的对象.这个A就是c的申明类
A d; // 定义一个d这个A类的对象.这个A就是d的申明类
a = new A(); // 实例化a对象,A是a的实例类
b = new B(); // 实例化b对象,B是b的实例类
c = new C(); // 实例化c对象,C是c的实例类
d = new D(); // 实例化d对象,D是d的实例类
a.Func(); // 执行a.Func:1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类A,就为本身 4.执行实例类A中的方法 5.输出结果 Func In A
b.Func(); // 执行b.Func:1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类B,有重写的 4.执行实例类B中的方法 5.输出结果 Func In B
c.Func(); // 执行c.Func:1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类C,无重写的 4.转去检查类C的父类B,有重载的 5.执行父类B中的Func方法 5.输出结果 Func In B
d.Func(); // 执行d.Func:1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类D,无重写的(这个地方要注意了,虽然D里有实现Func(),但没有使用override关键字,所以不会被认为是重写) 4.转去检查类D的父类A,就为本身 5.执行父类A中的Func方法 5.输出结果 Func In A
D d1 = new D();
d1.Func(); // 执行D类里的Func(),输出结果 Func In D a.Non_virtual(); //执行A类的Non_virtual
b.Non_virtual(); //执行A类的Non_virtual
c.Non_virtual(); //执行A类的Non_virtual
d.Non_virtual(); //执行A类的Non_virtual
d1.Non_virtual(); //执行D类的Non_virtual Console.ReadLine();
}
}
}
具体在检查的时候,遵循的规则,已经有人在网上做了很详细的阐述,我在这里引用一下:
虚拟函数从C#的程序编译的角度来看,它和其它一般的函数有什么区别呢?一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,也就是写死了的!而虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,其中那个申明时定义的类叫申明类,那个执行时实例化的类叫实例类。
如:飞禽 bird = new 麻雀();
那么飞禽就是申明类,麻雀是实例类。
具体的检查的流程如下
1、当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的函数是否为虚函数;
2、如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。
3、在这个实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过override关键字),如果是有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的函数。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚函数的父类为止,然后执行该父类里重载后的函数。
在上面的规则中,可以看到,如果子类没有override的修饰,那么就算父类是virtual的方法,子类的方法也无法被调用,而会去它的父类中找override的方法,直到找到祖先类。所以在面向对象的开发过程中,如果要实现Dependency Injection、IoC等设计模式,就必须非常留意类设计中继承方法的声明,否则很可能导致实际的程序运行与预期不符。
C# virtual和override的更多相关文章
- [C#] 區分 abstract、virtual、override 和 new
abstract.virtual.override和new是在類別的繼承關係中常用的四個修飾方法的關鍵字,在此略作總結. 1. 常用的中文名稱: n abstract => 抽象方法. n ...
- c#和java中的方法覆盖——virtual、override、new
多态和覆盖 多态是面向对象编程中最为重要的概念之一,而覆盖又是体现多态最重要的方面.对于像c#和java这样的面向对象编程的语言来说,实现了在编译时只检查接口是否具备,而不需关心最终的实现,即最终的实 ...
- C#Virtual和Override的几种组合
情况1: class A{public void Show()} class B:A{public void Show()} 编译通过,有警告让在B的方法里添加new关键字,以便将A的方法隐藏 编译时 ...
- new、virtual、override
我们先看下面一段程序: public class Father { public void Run0() { Console.WriteLine("Father.Run0"); } ...
- C#多态;父类引用指向子类对象;new和override的区别;new、abstract、virtual、override,sealed关键字区别和使用代码示例;c#类的初始化顺序
关于父类引用指向子类对象 例如: 有以下2个类 public class Father { public int age = 70; public static string name = " ...
- C#--virtual,abstract,override,new,sealed
virtual:使用此关键字,可以使其在派生类中被重写. abstract:抽象方法,由子类重写,或继续为抽象方法存在,并由其子子类实现. override: 重写父类方法,属性,或事件的抽象实现或虚 ...
- c#中关于virtual,override和new的理解
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...
- C#中的Virtual、Override和new关键词理解
来源:http://blog.csdn.net/jackiezhw/article/details/2673992 在 C# 中,派生类可以包含与基类方法同名的方法. 基类方法必须定义为 virtua ...
- Asp.Net中virtual、override理解
virtual关键字用于指定属性或方法在派生类中重写.默认情况下,派生类从其基类继承属性和方法,如果继承的属性或方法需要在派生类中有不同的行为,则可以重写它,即可以在派生类中定义该属性或方法的新实现, ...
随机推荐
- PBOC规范(2.0->3.0)对照表
1 数据方面 TAG PBOC2.0 ...
- PHP自定义函数使用外部变量
一般,php的自定义函数不能直接使用外部变量. 在php自定义函数中使用外部变量前,需要先使用global对外部变量进行声明. <?php $var = "hello World!& ...
- 41.使用Chipscope时如何防止reg_wire型信号被优化掉
随着FPGA设计复杂程度越来越高,芯片内部逻辑分析功能显得越来越重要.硬件层次上的逻辑分析仪价格十分昂贵,而且操作比较复杂.目前,FPGA芯片的两大供应商都为自己的FPGA芯片提供了软件层面上的逻辑分 ...
- 不同系统间传输float型数据
#include <stdio.h> #include <string.h> int main(void) { union result { float d; ...
- 微软职位内部推荐-Software Engineer II-Office Incubation
微软近期Open的职位: Office China team is looking for experienced engineers to improve consumer experience i ...
- powerdesigner 技巧
1.修改建表脚本生成规则.如果每个表格都有相同的字段,可以如下修改: Database -> Edit Current DBMS 展开 Script -> Object -> Tab ...
- Linux下各硬件装置的文件名
Linux对计算机各组件/装置的分辨,是和Windows系统完全不一样的!因为各个组件/设备在Linux上都是一个文件!因此,我们在认识各项设备时一定要学习Linux的设备文件名. 众所周知,Linu ...
- [收藏]Spring Security中的ACL
ACL即访问控制列表(Access Controller List),它是用来做细粒度权限控制所用的一种权限模型.对ACL最简单的描述就是两个业务员,每个人只能查看操作自己签的合同,而不能看到对方的合 ...
- C++编程显示四则运算题目
题目:C++编程显示四则运算题目 设计思路:(1)让用户自己确定出题的数量,同时显示加减乘除四则运算. (2)考虑到用户可能只会一种运算,因此可以选择运算.
- Mysql高级之存储过程
参考地址1:http://www.2cto.com/database/201411/350819.html 参考地址2:http://www.jb51.net/article/39471.htm my ...