谈谈C#中的三个关键词new , virtual , override(装载 Winner.Net)

C#支持单继承,说到继承就不得不说new,virtual和override这三个关键词,灵活正确的使用这三个关键词,可以使程序结构更加清晰,代码重用性更高。 
   

      以下是msdn中对new,virtual和override的定义:
      使用 new 修饰符显式隐藏从基类继承的成员。若要隐藏继承的成员,请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。
      virtual 关键字用于修改方法或属性的声明,在这种情况下,方法或属性被称作虚拟成员。虚拟成员的实现可由派生类中的重写成员更改。调用虚方法时,将为重写成员检查该对象的运行时类型。将调用大部分派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。默认情况下,方法是非虚拟的。不能重写非虚方法。
      不能将virtual 修饰符与以下修饰符一起使用:
      static    abstract    override
      使用 override 修饰符来修改方法、属性、索引器或事件。重写方法提供从基类继承的成员的新实现。由重写声明重写的方法称为重写基方法。重写基方法必须与重写方法具有相同的签名。不能重写非虚方法或静态方法。重写基方法必须是虚拟的、抽象的或重写的。
      重写声明不能更改虚方法的可访问性。重写方法和虚方法必须具有相同的访问级修饰符。
    
      不能使用下列修饰符修改重写方法:
      new   static    virtual   abstract
      重写属性声明必须指定与继承属性完全相同的访问修饰符、类型和名称,并且重写属性必须是虚拟的、抽象的或重写的。 
      可以稍微归纳一下:
      1. 对于基类中说明为虚的方法则必须在派生类中new或者override(注:对于基类的虚方法,虽然你在派生类中即不new也不override,但系统还是会提示你添关键字。否则系统将视其为隐藏。我们的意思是一样的,但总觉得明明确确写上关键字还是好些)。
      2. 如果用基类指针指向派生类对象的方式,动态匹配的源动力是virtual,而new和override都会阻止这种向下寻求匹配的行为,所以要使虚函数的性质得已保持下去,就要隐藏基类的虚方法,即在派生类中隐藏基类虚方法时,同时加以virtual关键字,使在多层次继承中能够调用到对象自身的版本。
      3.在多层次继承中,三个关键字使用次序有限定,new没有使用前提,即不管是普通方法、虚方法还是重写了的方法。virtual的使用,在它的基类不能有函数签名相同的方法,否则系统将提示添加new,即隐藏基类中的方法。virtual一般只出现一次,除非要在子类中隐藏父类的虚方法。

      override的使用是为了重写基类虚方法。
     

  上面的描述都很抽象,对于初学者可能不好理解,下面我将用示例来说明这三个用法和区别:此程序在vs2005下调试通过。其中有三个类,分别

为基类BaseClass,继承类InheritClass和继承类的继承类GrandsonClass代码分别如下:

  


//BaseClass.cs
namespace NewVirtualOverride
{
class BaseClass
{
public BaseClass()
{
}
publicvoid Print()
{
Console.WriteLine("BaseClassPrint");
}
}
}
//InheritClass.cs
namespace NewVirtualOverride
{
class InheritClass : BaseClass
{
public InheritClass():base()
{
}
publicvoid Print()
{
Console.WriteLine("InheritClassPrint");
}
}
}
//GrandsonClass.cs
namespace NewVirtualOverride
{
class GrandsonClass : InheritClass
{
public GrandsonClass():base()
{
}
publicvoid Print()
{
Console.WriteLine("GrandsonClassPrint");
}
}
}
//最后是主程序Program:
namespace NewVirtualOverride
{
class Program
{
staticvoid Main(string[] args)
{
BaseClass baseclass =new BaseClass();
baseclass.Print();
InheritClass inheritClass =new InheritClass();
inheritClass.Print();
Console.ReadLine();
}
}
}
运行这个程序会得到如下的结果:
BaseClassPrint
InheritClassPrint
其实细心的朋友在编译这个项目时,会发现出现了如下的警告提示:

 


class InheritClass:BaseClass
{
public InheritClass():base()
{}

//New Virtual Override InheritClass.Print()隐藏了继承的成员
//New Virtual Override BaseClass.Print()。如果是刻意隐藏请使用new
publicvoid Print()
{
Console.WriteLine("Hello");
}
}
大致意思是说,基类和继承类中有相同名字的方法,请在继承类中使用new来重新定义方法。这里的微妙之处在于,无论我们是隐式地指定new方法,还是显式的指定,new方法都与基类中的方法无关,在名称、原型、返回类型和访问修饰符方面都无关。
我们将程序中的Print()方法都变成new public void Print()后,上面的异常就不会发生了。再次运行程序,结果不变。new就是继承类使用与基类方法相同的名字对基类方法的重写。
下面我们看看virtual 和 override的搭配使用方法。
把BaseClass.cs改变如下:public virtual void Print();
把InheritClass.cs改变如下:public override void Print();
运行程序,结果如下:
BaseClassPrint
InheritClassPrint

 

     虽然结果与用new修饰符差不多,,但是其中的含意可不同,new是继承类对基类方法的重写而在继承类中产生新的方法,这时基类方法和继承方法之间没有任何的关系了,可是override就不同了,它也是对基类中方法的重写,但此时只是继承类重写了一次基类的方法。可以参考下面的例子来加深理解。

将Program.cs改变如下:

 


BaseClass baseclass =new BaseClass();
baseclass.Print();
InheritClass inheritClass =new InheritClass();
inheritClass.Print();
BaseClass bc =new InheritClass();
bc.Print();
分别运行用new修饰和用virtual/override修饰的程序,其结果如下:
    用new修饰的结果
BaseClassPrint
InheritClassPrint
BaseClassPrint
    用virtual/override修饰的结果:
BaseClassPrint
InheritClassPrint
InheritClassPrint
从上面的结果可以看出,在用new修饰的情况下,虽然bc是用InheritClass创建的实例,但是bc.Print()打印的还是BaseClassPrint,因为此时BaseClass和InheritClass中的Print已经是互不相同没有关系的两个方法了,而在virtual/override修饰的情况下,bc调用的Print方法已经被其子类override了,所以就打印了InheritClassPrint。

 最后我们再说说关键词之间的搭配关系,上面已经给出了virtual和override不兼容的几个关键词,这里就不重复了。我要说的是new和virtual在声明函数时,其实可以一块使用。因为这个函数是新的,故与其它任何new函数一样,隐藏了具有相同原型的继承来的函数。因为这个函数也是虚拟的,所以可以在派生类中进一步复位义,这样就为这个虚拟函数建立了一个新的基级别。最后用GrandsonClass类来看看。


将GrandsonClass.cs修改如下:
namespace NewVirtualOverride
{
class GrandsonClass : InheritClass
{
public GrandsonClass():base()
{
}
publicoverridevoid Print()
{
Console.WriteLine("GrandsonClassPrint");
}
}
}
InheritClass.cs修改如下:
namespace NewVirtualOverride
{
class InheritClass : BaseClass
{
public InheritClass():base()
{
}
newpublicvirtualvoid Print()
{
Console.WriteLine("InheritClassPrint");
}
}
}
BaseClass.cs修改如下:
namespace NewVirtualOverride
{
class BaseClass
{
public BaseClass()
{
}
publicvirtualvoid Print()
{
Console.WriteLine("BaseClassPrint");
}
}
}
Program.cs修改如下:
namespace NewVirtualOverride
{
class Program
{
staticvoid Main(string[] args)
{
BaseClass baseclass =new BaseClass();
baseclass.Print();

InheritClass inheritClass =new InheritClass();
inheritClass.Print();

BaseClass grandsonClass =new GrandsonClass();
grandsonClass.Print();\
Console.ReadLine();
}
}
}

 

运行结果为:
BaseClassPrint
InheritClassPrint
BaseClassPrint
可见在InheritClass中使用了new以后,就意味着它与基类的同名方法为两个不同方法了,而它又是虚拟的,所以它的子类还可以继续继承BaseClass的Print()方法。
将函数声明为virtual 与将它声明为new virtual是一样的,因为new仍然是默认的。所以下面的两句是相同的:
public new virtual void Print(); public virtual void Print();
那么new virtual的意义又在什么地方呢?在大型的层次结构中,这可能很有用,比如如下的System.Windows.Form类的继承关系Object->MarshalByRefObject->Component->Control->ScrollableControl->
ContainerControl。
很容易想象出将一个派生的窗体集合作为窗体对待,而不是作为Object的情形。
再将Program.cs修改如下:

namespace NewVirtualOverride
{
class Program
{
staticvoid Main(string[] args)
{
BaseClass baseclass =new BaseClass();
baseclass.Print();
InheritClass inheritClass =new InheritClass();
inheritClass.Print();
BaseClass grandsonClass1 =new InheritClass();
grandsonClass1.Print();
InheritClass grandsonClass2 =new GrandsonClass();
grandsonClass2.Print();
Console.ReadLine();
}
}
}

 

运行结果为:
BaseClassPrint
InheritClassPrint
BaseClassPrint
GrandsonClassPrint

转载于:https://www.cnblogs.com/zhcw/archive/2012/06/27/2565321.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不同类型数据所占的字节数

以下内容源于网络资源的学习与整理,如有侵权请告知删除。 数据类型的长度(所占的字节数),与机器字长及编译器都有关系。 所以,int、long int、short int等数据类型的长度可能随编译器而异。 几条铁定的原则&#xff08…

物联网概念升级,万物互联来袭

物联网概念尚在升温,万物互联又袭来。本月中旬,知名IT研究与咨询公司Gartner在2013 GartnerSymposium/Itxpo全球大会上向大家分享了他们对2014科技趋势的预测。在会上,Gartner提及的“万物互联”概念倍受科技界媒体关注。Gartner认为&#xf…

java遍历实体类的属性名称与值

//循环遍历OaInfoAssess实体中的属性与值for (Field field : oaInfoAssess.getClass().getDeclaredFields()){ //设置可以获取私人属性 field.setAccessible(true); try { Class type field.getType();// 得到此属性的类型 if(type String.class){ /…

unsigned char s1 : 2的用法

#include<stdio.h> #include<stdlib.h> //默认按照四字节对齐 //#pragma pack(1) union V {struct X{unsigned char s1 : 2;unsigned char s2 : 3;unsigned char s3 : 3;} x;unsigned char c; } v; //#pragma pack()int main(void) {v.c 100;//对应的二进制数字是…

MATLAB中排序函数sort()的用法

MATLAB中排序函数sort()可以对参数的元素进行升序排序或降序排序。 具体的用法如下&#xff1a; Ysort(X) sort()的参数可以是向量&#xff0c;矩阵&#xff0c;数组等等。当X是向量时&#xff0c;sort(X)对X的元素进行升序排序&#xff1b;当X是矩阵时&#xff0c;sort(X)对…

juc线程池原理(六):jdk线程池中的设计模式

一、jdk中默认线程池中的代理模式 单例类线程池只有一个线程&#xff0c;无边界队列&#xff0c;适合cpu密集的运算。jdk中创建线程池是通过Executors类中提供的静态的方法来创建的&#xff0c;其中的单例类线程池的方法如下&#xff1a; public static ExecutorService newSin…

Code First :使用Entity. Framework编程(6) ----转发 收藏

Chapter6 Controlling Database Location,Creation Process, and Seed Data 第6章 控制数据库位置&#xff0c;创建过程和种子数据 In previous chapters you have seen how convention and configuration can be used to affect the model and the resulting database schema.…

Hashtable的测试

Hashtable的测试 1 import java.util.Enumeration;2 import java.util.Hashtable;3 4 class TT {5 private String name null; //name和age是作为键的6 private Integer age 0;7 8 public TT(String name,int age) { //构造函数没有返回值9 this.nam…

截取AVI格式的视频C语言代码

首先在阅读本代码之前百度一下avi&#xff0c;虽然经过我验证上面有部分错误&#xff0c;但是不影响阅读。因为有些变量的注释我没有写&#xff0c;所以请读者自行搜索吧。下面是c语言文件&#xff0c;编译之后能够直接运行&#xff0c;用来截取开始时间&#xff08;单位s&…

C语言宏定义中UL的含义

1、U表示 unsigned 无符号&#xff0c;L表示 long 长整数。后缀大小写都可以&#xff0c;可以单独使用(100U)&#xff0c;也可以组合使用(100UL)。 2、F表示float&#xff0c;但是F不可以和U组合&#xff0c;因为浮点数没有unsigned。 3、后缀的作用是指明数据类型。因为单独…

查看CPU信息

1、查看物理cpu个数[rootwebserver ~]# cat /proc/cpuinfo|grep "physical id"|sort|uniq|wc -l 11. 查看物理CPU的个数#cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l2. 查看逻辑CPU的个数#cat /proc/cpuinfo |grep "processor"|wc …

计算多个文档之间的文本相似程度

首先我们上代码&#xff1a; from sklearn.feature_extraction.text import CountVectorizer corpus [ UNC played Duke in basketball, Duke lost the basketball game, I ate a sandwich ] vectorizer CountVectorizer(binaryTrue,stop_wordsenglish)#设置停用词为英语&…

判断操作系统的位数

#include<stdio.h>int main(void) {unsigned int num ~0;if (num 0xffffffff)printf("32");elseif (num 0xffff)printf("16");elseprintf("64");getchar();return 0; } 以上是判断一个操作系统是多少位的代码。 附另一种解法&#xf…

几个ubuntu16.04镜像下载地址

中科大源 http://mirrors.ustc.edu.cn/ubuntu-releases/16.04/ 阿里云开源镜像站 http://mirrors.aliyun.com/ubuntu-releases/16.04/ 兰州大学开源镜像站 http://mirror.lzu.edu.cn/ubuntu-releases/16.04/ 北京理工大学开源 http://mirror.bit.edu.cn/ubuntu-releases…

转】未指定 INSTANCESHAREDWOWDIR 命令行值。如果指定INSTANCESHAREDDIR 值,则必须指定该值 ....

插入光盘后不要用autorun的安装&#xff0c;使用命令行安装&#xff01;cd进安装光盘的根目录&#xff0c;输入命令&#xff1a;setup.exe /INSTALLSHAREDDIR"D://Program Files//Microsoft SQL Server//" /INSTALLSHAREDWOWDIR"D://Program Files (x86)//Micro…

C#使用StreamReader类读取文件文件

输入流用于从外部源读取数据。在很多情况下&#xff0c;数据源是磁盘上的文件或网络的某些位置。任何可以发送数据的位置都可以是数据源&#xff0c;比如网络应用程序、Web服务&#xff0c;甚至是控制台。 用来从文件中读取数据的类是StreamReader。同StreamWriter一样&#xf…

三种不使用中间参数,交换两个参数的值的方法

第一种&#xff1a;要求必须是整数 int i 50&#xff0c;j 60&#xff1b; i i^j; j i^j; i i^j;第二种&#xff1a;可以不是整数类型 i i j; j i - j; i i - j;第三种&#xff1a;很巧妙 i i j - (j i);

双边滤波

双边滤波 高斯滤波是最常用的图像去噪方法之一&#xff0c;它能很好地滤除掉图像中随机出现的高斯噪声&#xff0c;但是在之前的博客中提到过&#xff0c;高斯滤波是一种低通滤波&#xff08;有兴趣的点击这里&#xff0c;查看之前的博客&#xff09;&#xff0c;它在滤除图像中…

java代码做repeat次运算,从键盘输入几个数,比最值

总结&#xff1a;今天这个题目有点灵活&#xff0c;因为它不但要求输出结果&#xff0c;还要进行几次相同的输入&#xff0c;不退出循环 import java.util.Scanner;//从键盘一次输入更多的数&#xff0c;然后把每一次的数进行---可比较&#xff0c;或输出 public class ertyw {…

与C语言有关的面试题目

1. 用预处理指令#define 声明一个常数&#xff0c;用以表明1年中有多少秒&#xff08;忽略闰年问题&#xff09; #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 2. 写一个“标准”宏MIN&#xff0c;这个宏输入两个参数并返回较小的一个。 #define MIN(A,B) ((A) < (B) …