【C#基础概念】虚方法virtual
目录:
- 一、虚方法的概念
- 二、虚方法的特点
- 三、虚方法的作用
- 四、虚方法的声明
- 五、虚方法的执行
- 六、虚拟类的规则
一、虚方法的概念
在C#中,虚方法就是可以被子类重写的方法,如果子类重写了虚方法,则在运行时将运行重写的逻辑;如果子类没有重写虚方法,则在运行时将运行父类的逻辑。虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,其中那个申明时定义的类叫申明类,那个执行时实例化的类叫实例类。虚方法是用virtual关键字声明的
A a = new B(); 其中A是申明类,B是实例类。
二、虚方法的特点
- 虚方法前不允许有static,abstract,或override修饰符(因为静态的方法和抽象方法不能重写)
- 虚方法不能是私有的(因为在子类中要进行重写),所以不能使用private修饰符
- 不能在声明虚方法的同时指定重写虚方法,因为重写方法只能重写基类的虚方法,也就是要提前在基类中声明虚方法,所以virtual关键字和override关键字不能同时使用。
三、虚方法的作用
- 允许派生类(即其子类)对父类进行扩展。
- 虚方法是多态特性的一种体现。
- 增加开发过程中的可维护性,条理清晰明了。
四、虚方法的声明
在父类中:
|
1
2 3 4 |
public virtual 返回类型 方法名()
{ 方法体; } |
在子类中:
|
1
2 3 4 5 |
public override 返回值类型 方法名()
{ base.父类方法; 方法体; } |
五、虚方法的执行
一般方法在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,也就是写死了的!
虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数。
具体的检查流程如下:
- 当调用一个对象的方法时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的方法是否为虚方法。
- 如果不是虚函数,那么它就直接执行该函数。如果有virtual关键字,也就是一个虚方法,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。
- 在这个实例类里,它会检查这个实例类的定义中是否有实现该虚方法(通过new关键字)或是否有重新实现该虚方法(通过override关键字),如果有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的方法。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚方法的父类为止,然后执行该父类里重载后的方法。
下面我们通过具体实例看看:
例1:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class A
{ public virtual void Getreturn() { Console.WriteLine("我是A类的虚方法"); } } class Program { static void Main(string[] args) { A a = new A();//定义一个a这个A类的对象,这个A就是a的申明类,实例化a对象,A是a的实例类 a.Getreturn(); Console.ReadLine(); } } |
过程:1.先检查申明类A 2.检查到是Getreturn是虚拟方法 3.转去检查实例类A,结果是它本身 4.执行实例类A中实现Getreturn的方法 5.输出结果我是A类的虚方法
例2:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class A
{ public virtual void Getreturn() { Console.WriteLine("我是A类的虚方法"); } } class B : A { public override void Getreturn() //重新实现了虚方法 { Console.WriteLine("我是B类重写后的方法"); } } class Program { static void Main(string[] args) { A a = new B();//定义一个a这个A类的对象,这个A就是a的申明类,实例化a对象,B是a的实例类 a.Getreturn(); Console.ReadLine(); } } |
过程:1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类B,有重写的方法 4.执行实例类B中的方法 5.输出结果我是B类重写后的方法
例3:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class A
{ public virtual void Getreturn() { Console.WriteLine("我是A类的虚方法"); } } class B : A { public override void Getreturn() //重新实现了虚方法 { Console.WriteLine("我是B类重写后的方法"); } } class C : B { } class Program { static void Main(string[] args) { A a = new C();//定义一个a这个A类的对象,这个A就是a的申明类,实例化a对象,C是a的实例类 a.Getreturn(); Console.ReadLine(); } } |
过程:1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类C,无重写的方法 4.转去检查类C的父类B,有重写的方法5.执行父类B中的Getreturn方法 6.输出结果我是B类重写后的方法
例4:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class A
{ public virtual void Getreturn() { Console.WriteLine("我是A类的虚方法"); } } class B : A { public new void Getreturn() //覆盖父类里的同名函数,而不是重新实现 { Console.WriteLine("我是B类New方法"); } } class Program { static void Main(string[] args) { A a = new B();//定义一个a这个A类的对象,这个A就是a的申明类,实例化a对象,B是a的实例类 a.Getreturn(); Console.ReadLine(); } } |
过程:1.先检查申明类A 2.检查到是虚拟方法
3.转去检查实例类B,无重写的(这个地方要注意了,虽然B里有实现Getreturn(),但没有使用override关键字,所以不会被认为是重写)
4.转去检查类B的父类A,就是它本身 5.执行父类A中的Getreturn方法 6.输出结果我是A类的虚方法
例5:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class A
{ public virtual void Getreturn() { Console.WriteLine("我是A类的虚方法"); } } class B : A { public new void Getreturn() //覆盖父类里的同名函数,而不是重新实现 { Console.WriteLine("我是B类New方法"); } } class Program { static void Main(string[] args) { B b = new B();//定义一个b这个B类的对象,这个B就是b的申明类,实例化b对象,B是b的实例类 b.Getreturn(); Console.ReadLine(); } } |
过程:1.先检查申明类B 2.检查到不是虚拟方法 3.执行B类里的Getreturn() 4.输出结果我是B类New方法
可以使用抽象方法重写基类中的虚方法
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class A
{ public virtual void Getreturn() { Console.WriteLine("A类的虚方法"); } } abstract class B : A { public abstract override void Getreturn();//使用override修饰符,表示抽象重写了基类中该函数的实现 } abstract class C : A { public abstract new void Getreturn();//使用new修饰符显式声明,表示隐藏了基类中该函数的实现 } |
密封类可以重写基类中的虚方法(基类中的虚方法将隐式的转化为非虚方法,但密封类本身不能再增加新的虚方法)
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class A
{ public virtual void Getreturn() { Console.WriteLine("A类的虚方法"); } } sealed class Program:A { public override void Getreturn() { Console.WriteLine("Program类重写后的方法"); } static void Main(string[] args) { Program p = new Program(); p.Getreturn(); Console.ReadLine(); } } |
虚方法和new
new关键用于隐藏父类的成员,所有可以用new关键字隐藏父类的虚拟方法。
abstract和虚拟方法
抽象方法是隐式的虚拟方法,所有抽象类的非抽象子类可以用new 和override关键来重写abstract 方法
【C#基础概念】虚方法virtual的更多相关文章
- Delphi之静态方法,虚方法virtual,动态dynamic,抽象abstract,消息
Delphi之静态方法,虚方法virtual,动态dynamic,抽象abstract,消息 http://www.cnblogs.com/zhwx/archive/2012/08/28/266055 ...
- 虚方法(virtual)和抽象方法(abstract)的和接口(interface)的区别
虚方法(virtual)和抽象方法(abstract)的区别 2017年06月15日 13:41:26 阅读数:65 注:本文转载自 http://www.cnblogs.com/michaelxu/ ...
- C#虚方法virtual详解
转: http://www.cnblogs.com/jason_yjau/archive/2009/08/25/1553949.html C#虚方法virtual详解 在C++.Java等众多OOP语 ...
- 4.C#虚方法virtual详解
C#虚方法virtual详解 在C++.Java等众多OOP语言里都可以看到virtual的身影,而C#作为一个完全面向对象的语言当然也不例外. 虚拟函数从C#的程序编译的角度来看,它和其它一般的函数 ...
- 抽象方法(abstract method) 和 虚方法 (virtual method), 重载(overload) 和 重写(override)的区别于联系
1. 抽象方法 (abstract method) 在抽象类中,可以存在没有实现的方法,只是该方法必须声明为abstract抽象方法. 在继承此抽象类的类中,通过给方法加上override关键字来实现 ...
- 虚方法virtual详解
虚方法virtual详解 从C#的程序编译的角度来看,它和其它一般的函数有什么区别呢?一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,也就是写死了的!而虚函数在 ...
- 虚方法(virtual)
虚方法(virtual) Virtual 关键字用于修饰方法.属性.索引器或事件声明,并且允许在派生类中重写这些对象. 看一段代码: using System ; class A { public v ...
- 虚方法(virtual)\抽象方法(abstract)\接口(interface)的区别
转自:https://www.cnblogs.com/fantaohaoyou/p/9402657.html 虚方法和抽象方法都可以供派生类重写,它们之间有什么区别呢? 1. 虚方法必须有实现部分,抽 ...
- [转]虚方法(virtual)和抽象方法(abstract)的区别
虚方法和抽象方法都可以供派生类重写,它们之间有什么区别呢? 1. 虚方法必须有实现部分,抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化.如: //抽象方法pub ...
随机推荐
- 针对vue中请求数据对象新添加的属性不能响应式的解决方法
1.需要给对象添加属性时,不能采用传统的obj.属性=值,obj[属性]=值 来添加属性,在vue页面时需要这样使用 this.$set(obj,"propertyName",&q ...
- golang中channel
1. Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication). 2. select package main im ...
- 大厂面试:一个四年多经验程序员的BAT面经(字节、阿里、腾讯)
前言 上次写了篇欢聚时代的面经,公众号后台有些读者反馈说看的意犹未尽,希望我尽快更新其他大厂的面经,这里先说声抱歉,不是我太懒,而是项目组刚好有个活动要赶在春节前上线,所以这几天经常加班,只能工作之余 ...
- 什么是以特性为核心的持续交付|阿里巴巴DevOps实践指南
编者按:本文源自阿里云云效团队出品的<阿里巴巴DevOps实践指南>,扫描上方二维码或前往:https://developer.aliyun.com/topic/devops,下载完整版电 ...
- 「数据结构」Link-Cut Tree(LCT)
#1.0 简述 #1.1 动态树问题 维护一个森林,支持删除某条边,加入某条边,并保证加边.删边之后仍然是森林.我们需要维护这个森林的一些信息. 一般的操作有两点连通性,两点路径权值和等等. #1.2 ...
- 在Linux虚拟机上挂载文件卷
一 通过跳板机 将卷挂载在ec2 实例上的方法. 1 查询 机器上挂载了那些卷? // lsblk 是否已经是挂载卷 查看后面的目录 如果没有就是未挂载. 2 操作未挂载卷? /* sudo file ...
- 如何在pyqt中使用 QGraphicsView 实现图片查看器
前言 在 PyQt 中可以使用很多方式实现照片查看器,最朴素的做法就是重写 QWidget 的 paintEvent().mouseMoveEvent 等事件,但是如果要在图像上多添加一些形状,那么在 ...
- 理解https中的安全及其实现原理
Google的一份网络上的 HTTPS 加密透明报告(数据截至2022年1月)中指出HTTPS 连接的普及率在过去几年激增,互联网上排名前 100 位的非 Google 网站HTTPS 使用情况为:9 ...
- nested exception is java.lang.NoClassDefFoundError: org/fusesource/hawtbuf/UTF8Buffer
前言:IDE管理maven项目,总是遇到各种莫名奇妙的问题,有的是导入了依赖,IDE确报包未找到,有的是IDE显示找到,但是控制台确报未找到,有以下几种方法可以解决 第一:确认自己导入的依赖是否有问题 ...
- 男孩和女孩(二)-->相识
转载请注明来源:https://www.cnblogs.com/hookjc/ 那天是男孩的十九岁生日:男孩还是像平常一样,一大早就起来了(快七点了).一切都是那么的平常,直到第一节课下课,男孩的同窗 ...