c# 中 Abstract(虚方法)和 Virtual (抽象方法)都与继承有关,并且涉及override的使用。两者比较容易混淆,下面讨论一下二者的区别。
一、virtual方法
virtual 关键字用于在基类中修饰方法。
virtual的使用会有两种情况:
情况1:在基类中定义了virtual方法,但在派生类中没有重写该虚方法。那么在对派生类实例的调用中,该虚方法使用的是基类定义的方法。
情况2:在基类中定义了virtual方法,然后在派生类中使用override重写该方法。那么在对派生类实例的调用中,该虚方法使用的是派生重写的方法。
二、abstract方法
abstract关键字只能用在抽象类中修饰方法,并且没有具体的实现。抽象方法的实现必须在派生类中使用override关键字来实现。
接口和抽象类最本质的区别:抽象类是一个不完全的类,是对象的抽象;而接口是一种行为(函数、方法)规范。
三、关键字含义的比较
static:当一个方法被声明为static时,这个方法是一个静态方法,编译器会在编译时保留这个方法的实现。也就是说,这个方法属于类,但是不属于任何成员,不管这个类的实例是否存在,它们都会存在。就像入口函数static void Main,因为它是静态函数,所以可以直接被调用。
virtual:当一个方法被声明为virtual时,它是一个虚拟方法,直到你使用 ClassName variable = new ClassName(); 声明一个类的实例之前,它都不存在于真实的内存空间中。这个关键字在类的继承中非常常用,用来提供类方法的多态性支持。virtual,abstract是告诉其它想继承于他的类 你可以重写我的这个方法或属性。
overrride:表示重写这个继承于基类的某个virtual方法。
abstract:抽象方法声明使用,是必须被派生类覆盖的方法,抽象类就是用来被继承的;可以看成是没有实现体的虚方法。如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他一般方法;抽象类不能被直接被创建为对象实体的。
四、重写和覆盖
重写
用关键字 virtual 修饰的方法,叫虚方法。可以在子类中用override 声明同名的方法。
相应的没有用virtual修饰的方法,我们叫它实方法。
public class C1 { public virtual string GetName() { return "徐明祥"; } } public class C2 : C1 { public override string GetName() { return "xumingxiang"; } } C1 c1 = new C1(); Console.WriteLine(c1.GetName());//输出“徐明祥” C2 c2 = new C2(); Console.WriteLine(c2.GetName());//输出“xumingxiang” //重点看这里 C1 c3 = new C2(); Console.WriteLine(c3.GetName());//输出“xumingxiang”
覆盖
在子类中用 new 关键字修饰 定义的与父类中同名的方法。
覆盖不会改变父类方法的功能。
public class C1 { public string GetName() { return "徐明祥"; } } public class C2 : C1 { public new string GetName() { return "xumingxiang"; } } C1 c1 = new C1(); Console.WriteLine(c1.GetName());//输出“徐明祥” C2 c2 = new C2(); Console.WriteLine(c2.GetName());//输出“xumingxiang” //重点看这里,和上面的重写作比较 C1 c3 = new C2(); Console.WriteLine(c3.GetName());//输出“徐明祥”
说明:
当用父类装箱子类对象时,如 C1 c3 = new C2():重写会改变父类该函数委托链(和指针数组类似)的指向,即调用子类的该重写后的函数;而覆盖不会,仍然调用父类的函数。
关于重写和覆盖的使用:重写使用的频率比较高,实现多态; 覆盖用的频率比较低,如对以前无法修改的类进行继承的情景。
抽象方法、虚方法、接口,可以被重写(override);实方法不可以。
虚、实方法都可以被覆盖(new);而抽象方法,接口 不可以。
参考文章
C#中Abstract和Virtual的区别,主要内容出处。
C#中的重写和覆盖的区别,诺-诺,15-8。
更多相关阅读,请参考本人文章 C# 类的多态、结构、接口、抽象、虚函数总结。