C# virtual 函数
C# virtual 是虚拟的含义,在 C# 语言中,默认情况下类中的成员都是非虚拟的,通常将类中的成员定义成虚拟的,表示这些成员将会在继承后重写其中的内容。
virtual 关键字能修饰方法、属性、索引器以及事件等,用到父类的成员中。
使用 virtual 关键字修饰属性和方法的语法形式如下。
//修饰属性
public virtual 数据类型 属性名{get; set; }
//修饰方法
访问修饰符 virtual 返回值类型方法名
{
语句块;
}
需要注意的是,virtual 关键字不能修饰使用 static 修饰的成员。
此外,virtual 关键字既可以添加到访问修饰符的后面,也可以添加到访问修饰符的前面,但实际应用中习惯将该关键字放到访问修饰符的后面。
子类继承父类后能重写父类中的成员,重写的关键字是 override。
所谓重写是指子类和父类的成员定义一致,仅在子类中增加了 override 关键字修饰成员。
例如在父类中有一个求长方形面积的方法,方法定义如下。
- publie int Area(int x, int y)
- {
- return x * y
- }
在子类中重写该方法的代码如下。
public override int Area(int x,int y)
{
语句块;
return 整数类型的值;
}
在子类中重写父类中的方法后能改变方法体中的内容,但是方法的定义不能改变。
【实例 1】将上一节《C# base》中定义的 Person 类中的 Print 方法更改为虚拟的方法,分别用 Student 类和 Teacher 类继承 Person 类,并重写 Print 方法,打印出学生信息和教师信息。
为了减少重复的代码,在每个类中省略了属性部分的定义内容,仅保留 Print 方法部分的内容,实现的代码如下。
- class Person
- {
- public virtual void Print()
- {
- Console.WriteLine("编号:"+ Id);
- Console.WriteLine("姓名:"+ Name);
- Console.WriteLine("性别:"+ Sex);
- Console.WriteLine("身份证号:"+ Cardid);
- Console.WriteLine("联系方式:"+ Tel);
- }
- }
- class Student:Person
- {
- public override void Print()
- {
- Console.WriteLine("编号:"+ Id);
- Console.WriteLine("姓名:"+ Name);
- Console.WriteLine("性别:"+ Sex);
- Console.WriteLine("身份证号:"+ Cardid);
- Console.WriteLine("联系方式:"+ Tel);
- Console.WriteLine("专业:"+ Major);
- Console.WriteLine("年级:"+ Grade);
- }
- }
- class Teacher:Person
- {
- public override void Print()
- {
- Console.WriteLine("编号:"+ Id);
- Console.WriteLine("姓名:"+ Name);
- Console.WriteLine("性别:"+ Sex);
- Console.WriteLine("身份证号:"+ Cardid);
- Console.WriteLine("联系方式:"+ Tel);
- Console.WriteLine("专业:"+ Major);
- Console.WriteLine("年级:"+ Grade);
- }
- }
通过上面的代码即可完成对 Person 类中 Print 方法的重写,在重写后的 Print 方法中能直接调用在 Person 类中定义的成员。
但读者会发现在 Person 类的 Print 中已经对 Person 中的相关属性编写了输出操作的代码,而每一个子类中又重复地编写了代码,造成代码的冗余,也没有体现出代码重用的特点。
如果能在重写父类方法的同时直接使用父类中已经编写过的内容就会方便很多。
在重写 Print 方法后仍然需要使用 base 关键字调用父类中的 Print 方法执行相应的操作。
【实例 2】改写实例 1 中的 Student 和 Teacher 类中重写的 Print 方法,使用 base 关键字调用父类中的 Print 方法。
根据题目要求,更改后的代码如下。
- class Student:Person
- {
- public override void Print()
- {
- base.Print ();
- Console.WriteLine("专业:"+ Major);
- Console.WriteLine("年级:"+ Grade);
- }
- }
- class Teacher:Person
- {
- public override void Print()
- {
- base.Print ();
- Console.WriteLine("专业:"+ Major);
- Console.WriteLine("年级:"+ Grade);
- }
- }
从上面的代码可以看出继承给程序带来的好处,不仅减少了代码的冗余,还增强了程序的可读性。
方法隐藏和重写方法有区别吗?这是很多初学者常问的问题。观察以下代码,思考结果会是什么?
- class Program
- {
- static void Main(string[] args)
- {
- A a1 = new B();
- a1.Print();
- A a2 = new C();
- a2.Print();
- }
- }
- class A
- {
- public virtual void Print()
- {
- Console.WriteLine("A");
- }
- }
- class B :A
- {
- public new void Print()
- {
- Console.WriteLine("B");
- }
- }
- class C :A
- {
- public override void Print()
- {
- Console.WriteLine("C");
- }
- }
执行上面的代码,效果如下图所示。

从上面的执行效果可以看出,使用方法隐藏的方法调用的结果是父类 A 中 Print 方法中的内容,而使用方法重写的方法调用的结果是子类 C 中 Print 方法中的内容。
因此方法隐藏相当于在子类中定义新方法,而方法重写则是重新定义父类中方法的内容。
从上面的代码也可以看出,在“A a1=new B()”语句中 A 类是父类、B 类是子类,相当于将子类转换成父类,即隐式转换。
如果需要将父类转换成子类,则需要强制转换,并且在强制转换前需要先将所需的子类转换成父类,示例代码如下。
- A a2=new C();
- C c=(C) a2;
- c.Print();
在上面的实例中,a2 是父类对象,然后将其强制转换成 C 类对象。
Object 类中的 ToString 方法能被类重写,并返回所需的字符串,通常将其用到类中返回类中属性的值。
在 Student 类中添加重写的 ToString 方法,代码如下。
- class Student
- {
- public string Major{ get; set;}
- public string Grade{ get; set;}
- public void Print()
- {
- Console.WriteLine("专业:"+ Major);
- Console.WriteLine("年级:"+ Grade);
- }
- public override string ToString()
- {
- return Major+","+Grade;
- }
- }
这样,在调用 Student 类中的 ToString 方法时即可获取专业和年级的值。
此外,除了 ToString 方法,在类中也可以重写 Equals 方法、GetHashCode 方法。
C# virtual 函数的更多相关文章
- 09——绝不在构造和析构函数中调用virtual函数
在base class构造期间,virtual函数不是virtual函数. 构造函数.析构函数中不要调用virtual函数.
- 考虑virtual函数以外的选择
在C++中,有四种选择可以替代virtual函数的功能: 1.non-virtual interface(NVI)手法,这是一种template method模式.它以public non-virtu ...
- Effective C++ -----条款35:考虑virtual函数以外的其他选择
virtual函数的替代方案包括NVI手法及Strategy设计模式的多种手法.NVI手法自身是一个特殊形式的Template Method设计模式. 将机能从成员函数移到class外部函数,带来的一 ...
- Effective C++ -----条款09:绝不在构造和析构过程中调用virtual函数
在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层).
- 为什么内联函数,构造函数,静态成员函数不能为virtual函数
http://blog.csdn.net/freeboy1015/article/details/7635012 为什么内联函数,构造函数,静态成员函数不能为virtual函数? 1> 内联函数 ...
- 条款9:不要在构造和析构过程中调用virtual函数
如下是一个股票交易的例子: class Transaction // 交易的基类 { public: Transaction(); ; // 用于记录交易日志 }; Transaction::Tran ...
- effective c++:virtual函数的替代方案
绝不重新定义继承来的缺省值 首先明确下,虚函数是动态绑定,缺省参数值是静态绑定 // a class for geometric shapes class Shape { public: enum S ...
- effective c++:virtual函数在构造函数和析构函数中的注意事项
如不使用自动生成函数要明确拒绝 对于一个类,如果你没有声明,c++会自动生成一个构造函数,一个析构函数,一个copy构造函数和一个copy assignment操作符. class Empty { p ...
- 考虑virtual函数以外的其它选择
详情见<Effective C++>item35 1.使用non-virtual interface(NVI)手法,这是Template Method设计模式的一种特殊形式. 它以publ ...
- 还原virtual函数的本质-----C++
当你每次看到C++类中声明一个virtual函数,特别是看到了一个virtual的虚构函数.你知道它的意思吗?你肯定会毫不犹豫的回答:不就是多态么...在运行时确定具体的行为么...完全正确,但这里我 ...
随机推荐
- LaTeX技巧002:\section{}章节命令添加中文编译不了问题
在宏包hyperref前面添加选项CJKbookmarks \usepackage[CJKbookmarks]{hyperref} 并且把这句话尽可能地加在导言区的最底部,即尽可能靠近 \begin{ ...
- MongoDB,使用C#实现2d地理位置检索
这两天在研究mongoDB,从零开始接触它,为什么要研究它呢,因为它支持2d地图索引,而且速度非常快,可以用它来做类似微信的(摇一摇功能),不过网上很难搜到.net操作的,而且就算搜索到了也不能用,也 ...
- Android Q 使用通知栏消息
String myChannelId = "iot"; String myChannelName = "告警通知服务"; NotificationManager ...
- [CF1304F] Animal Observation - dp,单调队列
设 \(f[i][j]\) 为第 \(i\) 天在第 \(j\) 个位置放置的最大值,设 \(s[i][j]\) 是第 \(i\) 行的前缀和,则 \[ \begin{align} f[i][j] & ...
- 我的翻译--GSMem:通过GSM频率从被物理隔离的计算机上窃取数据
抽象概念 AG网络是指在物理上与公共互联网断开的网络.虽然近几年人们验证了入侵这类网络系统的可行性,但是从这种网络上获取数据仍然是一个有挑战的任务.在本文中,我们介绍GSMem,它是一个可以在蜂窝数据 ...
- Java面向对象--类和对象
面向对象是相对于面向过程而言的,是软件开发方法.面向对象把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统设计,更贴近事物的自然运行模式.本篇博客介绍Java面向对象的类和对象 目录: 面 ...
- SIFT算法原理(3)-确定关键点的主方位,构建关键点描述符
介绍官网:https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_sift_intro/py_sift_intro.html ...
- day30 NFS服务器概述
02. NFS存储服务概念介绍 NFS是Network File System的缩写,中文意思是网络文件共享系统, 它的主要功能是通过网络(一般是局域网)让不同的主机系统之间可以共享文件或目录 存储服 ...
- 谷歌浏览器安装jsonview
1.下载JsonView扩展程序压缩包 下载链接:https://pan.baidu.com/s/1B0IkrHHyTiYtwJEGE_halA 提取码:myi6 2.解压这个压缩包 3.打开谷歌浏览 ...
- Spring学习(六)
AOP和OOP 1.OOP:Object-Oriented Programming,面向对象程序设计,是静态的,一旦类写死了就不能改变了,要更改就得修改代码重新编译,父类类型引用指向对象来实现动态性. ...