上个星期由于要优化一个公共方法,使用到了虚方法和重写,由于之前一直对OOP的一些概念不是很清楚,借这个机会好好学习一下。
C#中的多态性在实现时主要是通过在子类(派生类)中重写基类的虚方法或函数成员来实现的,那么这里就遇到两个概念,一个是虚方法,另一个是重写方法,而这两个方法也是多态中最重要的两个概念,下面分别对它们进行讲解。
1.虚方法
虚方法就是允许被其子类重新定义的方法,在声明时,需要使用virtual修饰符。
注意:
(1)virtual修饰符不能与static、abstract或者override修饰符同时使用;
(2)由于虚方法不能是私有的,所以,virtual修饰符不能与private修饰符同时使用。
例 使用virtual修饰符声明一个虚方法,用来计算两个数的和,代码如下:
public virtual int Add(int x, int y) //定义一个虚方法 { return x + y; //返回两个数的和 }
2.重写方法
如果一个方法声明中含有override修饰符,则称该方法为重写方法,它主要用来使用相同的签名重写继承的虚方法。虚方法主要用来引入新方法,而重写方法则使从基类继承而来的虚方法专用化(提供虚方法的具体实现)。
注意:
(1)override修饰符不能与new、static或者virtual修饰符同时使用,另外,重写方法只能用于重写基类中的虚方法,不能用来单独声明方法;
(2)重载和重写是不相同的,重载是指编写一个与已有方法同名,但参数列表不同的方法,而重写是指在派生类中重写基类的虚方法。
例 创建一个控制台应用程序,首先定义一个基类,并在其中定义一个虚方法,用来计算两个数的和;然后使Program类继承于BaseClass类,并在该类中重写基类中的虚方法,使其实现计算3个数的和;最后在Main方法中,使用派生类对象实例化基类的一个对象,并使用该基类对象调用派生类中的方法,实现计算3个数的和。代码如下:
1 class BaseClass //定义一个基类 2 { 3 public virtual int Add(int x, int y) //定义一个虚方法 4 { 5 return x + y; //返回两个数的和 6 } 7 } 8 9 class Program:BaseClass //定义一个派生类,继承于BaseClass 10 { 11 static int z = 0; //定义一个静态变量,用来作为第3个被加数 12 public override int Add(int x, int y) //重写基类中的虚方法 13 { 14 return base.Add(x, y) + z; //计算3个数的和 15 } 16 17 static void Main(string[] args) 18 { 19 z = 698; //为静态变量赋值 20 21 BaseClass baseclass = new Program(); //使用派生类对象实例化基类对象 22 Console.WriteLine(baseclass.Add(98, 368)); //调用派生类中重写之后的方法 23 Console.ReadLine(); 24 } 25 }
说明:
在Main方法中使用基类对象调用的Add方法是在派生中重写之后的方法,这主要是因为虚方法的实现由派生类中的重写方法进行了取代。
技巧:
在派生类中重写基类中的虚方法时,可以使用base关键字调用基类中的虚方法。
3. 抽象方法
在C#中使用关键字 abstract 来定义抽象类和抽象方法。abstract 修饰符可以和类、方法、属性、索引器及事件一起使用。在类声明中使用 abstract 修饰符以指示某个类只能是其他类的基类。标记为抽象或包含在抽象类中的成员必须通过从抽象类派生的类来实现。
抽象类具有以下特性:
1、抽象类不能实例化。
2、抽象类可以包含抽象方法和抽象访问器。
3、不能用 sealed(C# 参考)修饰符修改抽象类,这意味着抽象类不能被继承。
4、从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实实现。
5、抽象类可以是抽象方法和实例方法。
在方法或属性声明中使用 abstract 修饰符以指示方法或属性不包含实现。
抽象方法具有以下特性:
1、抽象方法是隐式的虚方法。
2、只允许在抽象类中使用抽象方法声明。
3、因为抽象方法声明不提供实际的实现,所以没有方法体;方法声明只是以一个分号结束,并且在签名后没有大括号 ({ })。例如:
public abstract void MyMethod();
4、实现由一个重写方法提供,此重写方法是非抽象类的成员。
5、在抽象方法声明中使用 static 或 virtual 修饰符是错误的。
1 在此例中,类 Square 必须提供 Area 的实现,因为它派生自 ShapesClass: 2 3 abstract class ShapesClass 4 5 { 6 7 abstract public int Area(); 8 9 } 10 11 class Square : ShapesClass 12 13 { 14 15 int x, y; 16 17 18 19 public override int Area() 20 21 { 22 23 return x * y; 24 25 } 26 27 }
最后来讲一讲抽象方法和虚方法之间的区别:
区别:
1、虚方法必须有实现部分,抽象方法不可以有实现部分;
2、虚方法可以在派生类中重写也可以不重写,抽象方法必须在派生类中重写
3、虚方法可以在任何非密封类中声明,抽象方法只能在抽象类中声明。
4、如果类包含抽象方法,那么该类也必须为抽象的,不能实例化。
相比而言,虚方法倾向于代码复用,抽象方法更类似一种规约来约束子类必须实现某方法。
举个例子(未必恰当、只为说明问题):
比如有个基类“动物”;两个子类“狮子”、“青蛙”。
狮子捕猎:锁定目标、用牙齿和利爪抓获;
狮子说话:噢呜;
青蛙捕猎:锁定目标、用舌头抓获;
青蛙说话:呱呱;
对于捕猎,他们有共性也有区别:
所以就可以把捕猎声明为虚方法,基类里实现共性部分、各子类实现个性部分;
对于说话,完全不同,但是又必须让他们说话——否则成植物了,呵呵:
所以就可以把说话声明为抽象方法,基类只声明此方法来作为约束,强制子类实现。