我们来看一个例子
abstract class Base
{
private string a="base a";
public void ShowA()
{
Console.WriteLine("{0}",a);
}
public abstract void Show();
}
class SubClass : Base
{
private string b="sub b";
public void ShowB()
{
Console.WriteLine("{0}",b);
}
public override void Show()
{
Console.WriteLine("{0}",b);
}
}
我们采用如下代码进行测试:
class Program
{
static void Main(string [] args)
{
Base obj=new SubClass();
obj.ShowA();
obj.Show();
}
}
运行结果:
base a
sub b
上面的例子中我们定义了抽象类Base和其子类SubClass。要注意这两个类中的方法,抽象类Base里面有一个抽象方法Show还有一个普通方法ShowA;子类Subclass中重写了抽象基类的Show方法,并定义了自己的ShowB方法。上面的测试代码证明了obj还是Base类型,因此不能调用SubClass的方法ShowB,而只能调用ShowA和Show方法。因为Show方法是重写过的,所以此方法在调用时会调用其子类实例的该方法。
如果这样写测试代码:
SubClass obj=new SubClass();
obj.ShowA();
obj.Show();
运行结果一样,obj对象为SubClass类型的,它拥有自定义的Show方法和继承来的ShowA方法,所以这两个方法它都可以调用。对于SubClass类型的obj对象来讲,就没有必要考虑那么多了,因为它里面没有抽象方法,所以它只能调用自身的方法。自身的方法可以包括自己定义的方法、继承来的普通方法、重写基类抽象方法三种,对于一个普通类通常只需用到这三种方法,你需要区别开来考虑。到此你可能已经对抽象方法的功能有了一个了解,此时你最大的疑问可能是为什么要这样?为什么要让基类对象去动态执行子类实例的方法?我们再来看一个例子,它是用普通继承无法实现的。假如我们现在要写一个赛车的游戏,假如有很多不同样式不同型号的车参加比赛,再假如起跑枪响以后这些赛车都开始跑(调用Run方法),如果再假如同时跑的车有很多辆,那么你如何创建和操作这些对象,是每一辆车都创建一个对象,还是?请看我们下面的代码:
using System;
namespace Test
{
public abstract class Car
{
public abstract void Run();
}
class BMPCar : Car
{
public override void Run()
{
Console.WriteLine("宝马K09,起跑!");
}
}
class FERRARICar : Car
{
public override void Run()
{
Console.WriteLine("法拉利FC50,起跑!");
}
}
class GMCCar : Car
{
public override void Run()
{
Console.WriteLine("通用XP89,起跑!");
}
}
class Program
{
static void Main(string [] args)
{
Car [] cars=new Car[3] { new BMPCar(),new FERRARICar(),new GMCCar() };
for(int i=0; i<cars.Length;i++)
{
cars[i].Run(); /*所有汽车起跑*/
}
}
}
}
运行结果:
宝马K09,起跑!
法拉利FC50,起跑!
通用XP89,起跑!
上面的例子中我们定义了一个数组来存放所有汽车对象,我们知道数组只能存放同类型的元素,我们就以Car类型来定义数组,因为Car是抽象基类,所以该数组恰好可以保存各种汽车的对象。好就好在我们将所有汽车跑的方法都抽象到了基类中,然后我们在子类里逐个重写实现。如此定义基类和子类,使抽象基类对象完全可以代表子类对象来使用。所以我们可以使用一个循环来操作所有的汽车。我们上面的示例只是涉及到三辆汽车,设想如果有几十甚至上百辆的汽车,那么用这种方法来操作这些对象要简单、方便得多。
抽象方法还有一个关键字可以定义,就是virtual关键字。此关键字可以定义虚方法,虚方法是在普通类里定义的,可以在普通类里实现抽象方法。虚方法除了定义的地方和abstract关键字不同外(前者只能在普通类中使用,后者只能在抽象类中使用),使用方式基本一样,如都需要使用override来重写才能实现抽象机制等。实际上virtual关键字实现抽象机制的方式是和C++里一样的,这种方式在C#里已经显得不合时宜,因为我们使用abstract可以实现更直观更合理的抽象机制。另外,不管是abstract定义的抽象方法还是virtual实现的虚方法,访问修饰符都不能是私有的(抽象类中默认的访问修饰符也是私有的),并且都不能是静态(static)方法。