C# virtual 是虚拟的含义,在 C# 语言中,默认情况下类中的成员都是非虚拟的,通常将类中的成员定义成虚拟的,表示这些成员将会在继承后重写其中的内容。

virtual 关键字能修饰方法、属性、索引器以及事件等,用到父类的成员中。

使用 virtual 关键字修饰属性和方法的语法形式如下。

//修饰属性
public  virtual  数据类型  属性名{get; set; }

//修饰方法
访问修饰符  virtual  返回值类型方法名
{
    语句块;
}

需要注意的是,virtual 关键字不能修饰使用 static 修饰的成员。

此外,virtual 关键字既可以添加到访问修饰符的后面,也可以添加到访问修饰符的前面,但实际应用中习惯将该关键字放到访问修饰符的后面。

子类继承父类后能重写父类中的成员,重写的关键字是 override。

所谓重写是指子类和父类的成员定义一致,仅在子类中增加了 override 关键字修饰成员。

例如在父类中有一个求长方形面积的方法,方法定义如下。

  1. publie int Area(int x, int y)
  2. {
  3. return x * y
  4. }

在子类中重写该方法的代码如下。

public override int Area(int x,int y)
{
    语句块;
    return  整数类型的值;
}

在子类中重写父类中的方法后能改变方法体中的内容,但是方法的定义不能改变。

【实例 1】将上一节《C# base》中定义的 Person 类中的 Print 方法更改为虚拟的方法,分别用 Student 类和 Teacher 类继承 Person 类,并重写 Print 方法,打印出学生信息和教师信息。

为了减少重复的代码,在每个类中省略了属性部分的定义内容,仅保留 Print 方法部分的内容,实现的代码如下。

  1. class Person
  2. {
  3. public virtual void Print()
  4. {
  5. Console.WriteLine("编号:"+ Id);
  6. Console.WriteLine("姓名:"+ Name);
  7. Console.WriteLine("性别:"+ Sex);
  8. Console.WriteLine("身份证号:"+ Cardid);
  9. Console.WriteLine("联系方式:"+ Tel);
  10. }
  11. }
  12. class Student:Person
  13. {
  14. public override void Print()
  15. {
  16. Console.WriteLine("编号:"+ Id);
  17. Console.WriteLine("姓名:"+ Name);
  18. Console.WriteLine("性别:"+ Sex);
  19. Console.WriteLine("身份证号:"+ Cardid);
  20. Console.WriteLine("联系方式:"+ Tel);
  21. Console.WriteLine("专业:"+ Major);
  22. Console.WriteLine("年级:"+ Grade);
  23. }
  24. }
  25. class Teacher:Person
  26. {
  27. public override void Print()
  28. {
  29. Console.WriteLine("编号:"+ Id);
  30. Console.WriteLine("姓名:"+ Name);
  31. Console.WriteLine("性别:"+ Sex);
  32. Console.WriteLine("身份证号:"+ Cardid);
  33. Console.WriteLine("联系方式:"+ Tel);
  34. Console.WriteLine("专业:"+ Major);
  35. Console.WriteLine("年级:"+ Grade);
  36. }
  37. }

通过上面的代码即可完成对 Person 类中 Print 方法的重写,在重写后的 Print 方法中能直接调用在 Person 类中定义的成员。

但读者会发现在 Person 类的 Print 中已经对 Person 中的相关属性编写了输出操作的代码,而每一个子类中又重复地编写了代码,造成代码的冗余,也没有体现出代码重用的特点。

如果能在重写父类方法的同时直接使用父类中已经编写过的内容就会方便很多。

在重写 Print 方法后仍然需要使用 base 关键字调用父类中的 Print 方法执行相应的操作。

【实例 2】改写实例 1 中的 Student 和 Teacher 类中重写的 Print 方法,使用 base 关键字调用父类中的 Print 方法。

根据题目要求,更改后的代码如下。

  1. class Student:Person
  2. {
  3. public override void Print()
  4. {
  5. base.Print ();
  6. Console.WriteLine("专业:"+ Major);
  7. Console.WriteLine("年级:"+ Grade);
  8. }
  9. }
  10. class Teacher:Person
  11. {
  12. public override void Print()
  13. {
  14. base.Print ();
  15. Console.WriteLine("专业:"+ Major);
  16. Console.WriteLine("年级:"+ Grade);
  17. }
  18. }

从上面的代码可以看出继承给程序带来的好处,不仅减少了代码的冗余,还增强了程序的可读性。

方法隐藏和重写方法有区别吗?这是很多初学者常问的问题。观察以下代码,思考结果会是什么?

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. A a1 = new B();
  6. a1.Print();
  7. A a2 = new C();
  8. a2.Print();
  9. }
  10. }
  11. class A
  12. {
  13. public virtual void Print()
  14. {
  15. Console.WriteLine("A");
  16. }
  17. }
  18. class B :A
  19. {
  20. public new void Print()
  21. {
  22. Console.WriteLine("B");
  23. }
  24. }
  25. class C :A
  26. {
  27. public override void Print()
  28. {
  29. Console.WriteLine("C");
  30. }
  31. }

执行上面的代码,效果如下图所示。

从上面的执行效果可以看出,使用方法隐藏的方法调用的结果是父类 A 中 Print 方法中的内容,而使用方法重写的方法调用的结果是子类 C 中 Print 方法中的内容。

因此方法隐藏相当于在子类中定义新方法,而方法重写则是重新定义父类中方法的内容。

从上面的代码也可以看出,在“A a1=new B()”语句中 A 类是父类、B 类是子类,相当于将子类转换成父类,即隐式转换。

如果需要将父类转换成子类,则需要强制转换,并且在强制转换前需要先将所需的子类转换成父类,示例代码如下。

  1. A a2=new C();
  2. C c=(C) a2;
  3. c.Print();

在上面的实例中,a2 是父类对象,然后将其强制转换成 C 类对象。

Object 类中的 ToString 方法能被类重写,并返回所需的字符串,通常将其用到类中返回类中属性的值。

在 Student 类中添加重写的 ToString 方法,代码如下。

  1. class Student
  2. {
  3. public string Major{ get; set;}
  4. public string Grade{ get; set;}
  5. public void Print()
  6. {
  7. Console.WriteLine("专业:"+ Major);
  8. Console.WriteLine("年级:"+ Grade);
  9. }
  10. public override string ToString()
  11. {
  12. return Major+","+Grade;
  13. }
  14. }

这样,在调用 Student 类中的 ToString 方法时即可获取专业和年级的值。

此外,除了 ToString 方法,在类中也可以重写 Equals 方法、GetHashCode 方法。

C# virtual 函数的更多相关文章

  1. 09——绝不在构造和析构函数中调用virtual函数

    在base class构造期间,virtual函数不是virtual函数. 构造函数.析构函数中不要调用virtual函数.

  2. 考虑virtual函数以外的选择

    在C++中,有四种选择可以替代virtual函数的功能: 1.non-virtual interface(NVI)手法,这是一种template method模式.它以public non-virtu ...

  3. Effective C++ -----条款35:考虑virtual函数以外的其他选择

    virtual函数的替代方案包括NVI手法及Strategy设计模式的多种手法.NVI手法自身是一个特殊形式的Template Method设计模式. 将机能从成员函数移到class外部函数,带来的一 ...

  4. Effective C++ -----条款09:绝不在构造和析构过程中调用virtual函数

    在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层).

  5. 为什么内联函数,构造函数,静态成员函数不能为virtual函数

    http://blog.csdn.net/freeboy1015/article/details/7635012 为什么内联函数,构造函数,静态成员函数不能为virtual函数? 1> 内联函数 ...

  6. 条款9:不要在构造和析构过程中调用virtual函数

    如下是一个股票交易的例子: class Transaction // 交易的基类 { public: Transaction(); ; // 用于记录交易日志 }; Transaction::Tran ...

  7. effective c++:virtual函数的替代方案

    绝不重新定义继承来的缺省值 首先明确下,虚函数是动态绑定,缺省参数值是静态绑定 // a class for geometric shapes class Shape { public: enum S ...

  8. effective c++:virtual函数在构造函数和析构函数中的注意事项

    如不使用自动生成函数要明确拒绝 对于一个类,如果你没有声明,c++会自动生成一个构造函数,一个析构函数,一个copy构造函数和一个copy assignment操作符. class Empty { p ...

  9. 考虑virtual函数以外的其它选择

    详情见<Effective C++>item35 1.使用non-virtual interface(NVI)手法,这是Template Method设计模式的一种特殊形式. 它以publ ...

  10. 还原virtual函数的本质-----C++

    当你每次看到C++类中声明一个virtual函数,特别是看到了一个virtual的虚构函数.你知道它的意思吗?你肯定会毫不犹豫的回答:不就是多态么...在运行时确定具体的行为么...完全正确,但这里我 ...

随机推荐

  1. gtid环境下mysqldump对于set-gtid-purged的取值

    gtid环境备份的时候,还在为set-gtid-purged=0|1的选择而烦恼吗,一起来分析一下. [mysql@lxd-vm1@/home/mysql]$ mysqldump --help | g ...

  2. 获取redis cluster master对应的slot分布情况

    需求:原生的redis-trib.rb功能是强大,但输出的内容过于繁杂,比如我需要关注哪些master对应哪些slots,不是很直观,如果集群的规模更大的话,那么输出的结果获取信息更加困难. 说明:这 ...

  3. 541-反转字符串 II

    541-反转字符串 II 给定一个字符串和一个整数 k,你需要对从字符串开头算起的每个 2k 个字符的前k个字符进行反转.如果剩余少于 k 个字符,则将剩余的所有全部反转.如果有小于 2k 但大于或等 ...

  4. layedit不可编辑,按钮不能使用

    $("#LAY_layedit_1").contents().find("body[contenteditable]").prop("contente ...

  5. 项目打jar包和运行

    打包成jar包和部署,运行. 1.在pom.xml中加入  <packaging>jar</packaging> <groupId>com.demo02</g ...

  6. 利用 Hexo + Github 搭建自己的博客

    扯在前面 在很久很久以前,一直就想搭建属于自己的一个博客,但由于各种原因,最终都不了了之,恰好最近突然有了兴趣,于是就自己参照网上的教程,搭建了属于自己的博客. 至于为什么要搭建自己的博客了?哈哈,大 ...

  7. Intellij IDEA史上最全快捷键大全

    古人有云:工欲善其事,必先利其器,要是只是手握利器,而不能发挥其最大的效益,那无异于赤手空拳,对敌对垒. 那古人所云,未得其精髓,只能为碎语闲言尔. 自动代码 常用的有fori/sout/psvm+T ...

  8. JS对象简介

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. R-CNN系列阅读笔记

    之前感觉研究的还是不够透彻,这次彻底从头到尾研究一下. R-CNN系列 R-CNN 本文发表于2014年. 背景及整体框架 背景:将CNN在图像分类领域的成功(2012年)应用于目标检测上面.检测问题 ...

  10. Win10 JDK 环境变量配置

    1.安装JDK 到指定的目录 2.配置环境变量 2.1   配置 CLASSPATH    .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; 2. ...