一、重写
通过为声明的方法提供新的实现,派生类可以重写基类的方法。并且基类中的这个方法必须标记为虚方法(virtual),Java和C#之间的一个重要区别在于,Java方法在默认情况下标记为虚方法,而在C#中,必须使用virtual修饰符才能将方法显式标记为虚方法。可以采用大致相同的方式重写属性访问器以及方法。
- 不能重写非虚方法或静态方法。重写的基方法必须是 virtual、abstract 或 override 的。为什么 override 也可以重写呢?因为基类中的 override实际上是对基类的基类进行的重写,由于继承可传递,所以也可以对基类中 override 的方法进行重写。
- override声明不能更改virtual方法的可访问性。override 方法和 virtual 方法必须具有相同的访问级别修饰符。
- 不能使用修饰符 new、static、virtual 或 abstract 来修改 override 方法。
- 重写属性声明必须指定与继承属性完全相同的访问修饰符、类型和名称,并且被重写的属性必须是 virtual、abstract 或 override 的。
- 可以在重写方法的中用base调用基类的方法
例子如下:(本文所有例子是一个关于动物的类、抽象类、接口的部分代码)
public class Dog : Animals,IAction { public Dog() { } public Dog(string name) { _name = name; } public override string Name { get { return _name; } } /*****省略部分代码****/ public virtual void FurColor(){ Console.WriteLine("Performing base class fur color"); } } class Pomeranian:Dog { public Pomeranian() { } public override void FurColor() { Console.WriteLine("Pomeranian's fur color is white."); //base.FurColor(); } } class Program { static void Main(string[] args) { Pomeranian dog1 = new Pomeranian(); dog1.FurColor();//调用new隐藏后的方法 Console.WriteLine(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ClassTest { public abstract class Animals { protected string _name; //声明抽象属性 public abstract string Name { get; } //声明抽象方法 public abstract void Show(); //实现虚方法 public virtual void MakeVoice() { Console.WriteLine("All animals can make voice."); } //实现一般的方法 public void Sleep() { Console.WriteLine("All the animals need to sleep."); } } }
设计到的接口IAction
public class Duck : Animals, IAction { public Duck(string name) { this._name = name; } //重写抽象属性 public override string Name { get { return _name; } } /******省略部分代码******/ //重写虚方法 public override void MakeVoice() { Console.WriteLine("I am a duck,ga....ga...."); } }
然后在Dog类中添加new隐藏方法
public new void MakeVoice()//用new关键字将虚方法隐藏 { base.MakeVoice();//调用基类方法 Console.WriteLine(" I'am a dog,wang...wang..."); }
在Main函数中写代码调用
/**省略了与这两个无关的代码* *Duck重写了Makevoice(),只能实现子类中的方法 *Dog隐藏了Makevoice(),但是父类还可以调用它的代码 */ Animals duck=new Duck("Duck"); duck.MakeVoice();//调用override重写后的虚方法 Animals dog=new Dog("Dog"); dog.MakeVoice();//直接调用基类虚方法(new 关键字方法隐藏) Dog dog1 = new Dog("dog1"); dog1.MakeVoice();//调用new隐藏后的方法
结果如下:
二、重载( overloading )
C#中同一个类不可以有两个相同的方法(方法名、参数类型、参数个数和参数位置都 相同)。但可以有方法名相同,参数不同(参数类型、参数个数和参数位置不相同)的方法。这 种相同的方法名,参数不同的方法称为重载。
决定方法是否构成重载有以下几个条件:
◆ 在同一个类中;
◆ 方法名相同;
◆ 参数列表不同。
例子:我们创建一个机器狗BattleRavage,让它可以计算加法
class BattleRavage:Dog { public BattleRavage():base("brDog") { } //这只狗有计算的能力,实现重载功能 public string add() { return "I'm ready!"; } public double add(double numberOne, double numberTwo) { return numberOne + numberTwo; } public double add(double numberOne, double numberTwo, double numberThree) { return numberOne + numberTwo + numberThree; } public new void Sleep() { Console.WriteLine("I don't need to go to"); } }
Main中实现:
//实现重载add() BattleRavage brDog = new BattleRavage(); brDog.Sleep();//调用new方法 Console.WriteLine("这条狗能做算术,很厉害!"); string dogReady = brDog.add(); double result1 = brDog.add(2.2, 5.5); double result2 = brDog.add(2, 4, 3); Console.WriteLine(dogReady); Console.WriteLine("2.2+5.5=" + result1.ToString()); Console.WriteLine("2+4+3=" + result2.ToString()); Console.WriteLine();
运行结果:
三、继承
为了提高软件模块的可复用性和可扩充性,以便提高软件的开发效率,我们总是希望能够利用前人或自己以前的开发成果,同时又希望在自己的开发过程中能够有足够的灵活性,不拘泥于复用的模块。
继承就是子类获得父类的成员和使用父类的方法。
C#中的继承符合下列规则:
1、继承是可传递的。如果C从B中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中的成员。Object 类作为所有类的基类。
2、派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义。
3、构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类能否访问它们。
4、派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。
5、类可以定义虚方法、虚属性以及虚索引指示器,它的派生类能够重载这些成员,从而实现类可以展示出多态性。
6、派生类只能从一个类中继承,可以通过接吕实现多重继承。
四、多态
多态则是为了避免在父类里大量重载引起代码臃肿且难于维护。
网上看到一个有趣的说法是:继承是子类使用父类的方法,而多态则是父类使用子类的方法。
多态性常被视为自封装和继承之后,面向对象的编程的第三个支柱。 Polymorphism(多态性)是一个希腊词,指“多种形态”,多态性具有两个截然不同的方面:
-
在运行时,在方法参数和集合或数组等位置,派生类的对象可以作为基类的对象处理。 发生此情况时,该对象的声明类型不再与运行时类型相同。
-
基类可以定义并实现虚方法,派生类可以重写这些方法,即派生类提供自己的定义和实现。 在运行时,客户端代码调用该方法,CLR 查找对象的运行时类型,并调用虚方法的重写方法。 因此,您可以在源代码中调用基类的方法,但执行该方法的派生类版本。
C#支持两种类型的多态性:
● 编译时的多态性
编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。
● 运行时的多态性
运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现。
编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。
例子:在基类Dog中添加一个显示皮毛颜色的方法,然后在各子类中重写该方法,最后通过基类去调用各子类的方法
public class Dog : Animals, IAction { public Dog() { } public Dog(string name) { _name = name; } public override string Name { get { return _name; } } public virtual void FurColor(){ Console.WriteLine("Performing base class fur color"); } } class Pomeranian:Dog { public Pomeranian() { } public override void FurColor() { Console.WriteLine("Pomeranian's fur color is white."); //base.FurColor(); } } class Labrador:Dog { public Labrador() { } public override void FurColor() { Console.WriteLine("Labrador's fur color is pale yellow."); //base.FurColor(); } } class Husky:Dog { public Husky() { } public override void FurColor() { Console.WriteLine("Husky' fur color is black and white mixed color."); base.FurColor(); } } class Program { static void Main(string[] args) { //实现多态,调用基类的方法,但执行该方法的派生类版本 List<Dog> dogs = new List<Dog>(); dogs.Add(new Husky()); dogs.Add(new Labrador()); dogs.Add(new Pomeranian()); foreach (Dog s in dogs) { s.FurColor(); } } }
运行结果:
总结:
多态性通过继承、重写、虚方法等来实现,重写就是在子类中写出和基类中不同的方法(该方法可以为virtual、abstract 或 override)实现(方法名,参数,返回值都一样),重载允许我们用相同的方法名实现不同的操作,继承则是子类获得父类的成员和使用父类的方法。
参考资料:
http://www.cnblogs.com/hliq/archive/2011/04/06/2087191.html
http://msdn.microsoft.com/zh-cn/library/ms173152.aspx