虚方法(virtual )
简单的说,虚方法就是可以被子类重写的方法,如果子类重写了虚方法,那么运行时将使用重写后的逻辑,如果没有重写,则使用父类中虚方法的逻辑。
1,非虚方法的实现是一成不变的,无论在基类和派生类上调用,实现均相同。
2,虚方法能够在派生类中进行重写
3,虚方法重写时,不可改变访问修饰符
public class class1 { public virtual void virtualMehtod() { Console.WriteLine("这是一个虚方法!"); } public void NOvirtualMehtod() { Console.WriteLine("这是一个非虚方法!"); } } //将父类设置为虚方法使用virtual关键字,子类需要复写父类的方法使用override关键字,如果没有复写,则使用父类中虚方法的逻辑。 public class class2:class1 { public override void virtualMehtod() { Console.WriteLine("这是一个新写的虚方法!"); } //因为子类中含有与父类相同的方法需要用new关键字显示隐藏父类的方法 public new void NOvirtualMehtod() { Console.WriteLine("这是一个新的方法!"); } } class Program { static void Main(string[] args) { class1 c1 = new class1(); class2 c2 = new class2(); c1.virtualMehtod(); c1.NOvirtualMehtod(); c2.virtualMehtod(); c2.NOvirtualMehtod(); //将c2对象赋值给c1,c1不会被c2覆盖 c1 = c2; c1.virtualMehtod(); c1.NOvirtualMehtod(); Console.Read(); } }
抽象类(abstract )
不能被直接实例化的类被叫做抽象类。抽象类中一般含有抽象方法,当然也可以有具体实现,继承类只有实现了所有抽象类中的抽象方法才能被实例化。抽象类只能通过接口和作为其它类的基类使用。
抽象类能够被用于类,方法,属性,索引器和事件,使用abstract 在一个类声明中表示该类倾向要作为其它类的基类成员被标示成abstract,或被包含进一个抽象类,必须被其派生类实现。
class Program { abstract class Drink { public abstract void drink(); //利用抽象类来实现类的抽象化,并且方法中不能有方法体{}
public void write() { Console.WriteLine("哈哈"); } } class Milk : Drink { public override void drink() { Console.WriteLine("我是牛奶,我可以解饿"); } } class Tea : Drink { public override void drink() { Console.WriteLine("我是茶,我可以解渴"); } } static void Main(string[] args) { //通过Drink做为基类分别来实例化 Tea类和Milk类。 Drink tea=new Tea(); Drink milk=new Milk(); Tea t1 = new Tea(); t1.drink(); Drink[] drink = { tea, milk }; foreach( Drink outvalue in drink ) { outvalue.drink(); } // Drink d = new Drink();//抽象类不能实例化 //Tea和Milk都可以直接调用write方法 tea.write(); milk.write(); Console.ReadKey(); } }
在C#中,一个抽象类能够继承另一个非抽象类,另外,继承了基类的方法,添加新的抽象和非抽象方法是可行的。
class MyClass1 // 这是一个非抽象类 { public void Method1() //非抽象方法 { Console.WriteLine("Method of a non-abstract class"); } } abstract class MyAbs : MyClass1 // 抽象类继承非抽象类 { public abstract void AbMethod1(); } class MyClass : MyAbs//MyClass类必须实现基类抽象方法 { public override void AbMethod1() { Console.WriteLine("Abstarct method #1 of MyClass"); } }
static void Main(string[] args) { MyClass mc = new MyClass(); mc.Method1(); mc.AbMethod1(); }
抽象方法有以下特征:
1.一个抽象方法可以看作是一个虚函数。
2.抽象方法的声明只能在抽象类中。
3.因为抽象方法声明只提供一个无实现的方式,没有方法体
4.方法体的实现被覆写方法提供,覆写方法是一个非抽象类的成员。
5.抽象属性的行为和抽象方法相像,除了不同的声明形式。
6.在一个静态属性中使用abstract 是一个错误。
*一个抽象属性能够通过派生类使用 override 实现.
对于抽象类:
一个抽象类必须为所有的接口成员提供实现
一个用于实现接口的抽象类可能把接口方法安排到抽象方法上。例如
interface I { void M(); } abstract class C : I { public abstract void M(); }
抽象类具有以下特征:
1.抽象类不能被实例化。
2.抽象类可以包含抽象方法和访问器
3.不能把抽象类用密封(sealed)来修饰,那就意味着类不能被继承,这违反抽象类被继承的原则。
4.一个派生于一个抽象类的非抽象类必须包括所有的继承来的抽象方法和访问器的实现
5.在方法和属性中使用abstract 关键字意味着包含它们的实现。
接口(interface)
接口包含方法或属性的声明,不包含具体的实现方法的代码,接口可以实现多继承,继承接口的类必须实现接口中声明的方法或属性,接口主要定义一种规范,统一调用方法,提供永远的接口,当类增加时,现有接口方法能够满足继承类中的大多数方法,没必要重新给新类设计一组方法,也节省了代码,提高了开发效率。在大型项目中发挥这重要的作用。
例子:
//公共接口: 飞的方法 interface IFlyable { void Fly(); } //抽象类:鸟类 abstract class Bird { //抽象一个吃的方法 public abstract void Eat(); } //老鹰类: 分别继承鸟类和接口飞 //老鹰同时拥有吃的功能和飞的功能。 class Eagle : Bird, IFlyable { public void Fly() { Console.WriteLine("我是老鹰我会飞"); } public override void Eat() { Console.WriteLine("我是老鹰,我抓吃小鸡"); } } //天鹅类 class Swan : Bird, IFlyable { public override void Eat() { Console.WriteLine("我是天鹅,我吃鱼"); } public void Fly() { Console.WriteLine("我是天鹅我会飞"); } } //麻雀类 class sparrow : Bird, IFlyable { public void Fly() { Console.WriteLine("我是麻雀我会飞"); } public override void Eat() { Console.WriteLine("我是麻雀,我专吃粮食"); } } //鸵鸟类,鸵鸟不会飞只继承吃的类。 class Ostrich : Bird { public override void Eat() { Console.WriteLine("我是鸵鸟,我专吃小鸡"); } } //气球类 气球只拥有飞的类 class Ballon : IFlyable { public void Fly() { Console.WriteLine("我是气球我会飞"); } } static void Main(string[] args) { //将继承IFlyable的类分别放入数组中 IFlyable[] flys = { new sparrow(), new Eagle(), new Swan(), new Ballon() }; foreach (IFlyable outFlys in flys) { outFlys.Fly(); //输出飞的方法 } Console.ReadKey(); }
接口使方法具有较好扩展性.
另外, 也有抽象类能够实现但接口不能实现的情况:
1. 在抽象类中可以加代码逻辑,但接口不能.
2. 如果要在接口中增加一个方法, 所有实现它的类都强制重载一遍此方法, 如果重载类很多时, 会增大工作量.
总之, 一般在仅实现单继承用途时, 尽量用抽象类, 功能也更强些!