一、定义:
多态是面向对象程序设计的又一个特性。在面向过程的程序设计中,主要工作是编写一个个的过程或函数,这些过程和函数不能重名。例如在一个应用中,需要对数值型数据进行排序,还需要对字符型数据进行排序,虽然使用的排序方法相同,但要定义两个不同的过程(过程的名称也不同)来实现。
在面向对象程序设计中,可以利用“重名”来提高程序的抽象度和简洁性。首先我们来理解实际的现象,例如,“启动”是所有交通工具都具有的操作,但是不同的具体交通工具,其“启动”操作的具体实现是不同的,如汽车的启动是“发动机点火——启动引擎”、“启动”轮船时要“起锚”、气球飞艇的“启动”是“充气——解缆”。如果不允许这些功能使用相同的名字,就必须分别定义“汽车启动”、“轮船启动”、“气球飞艇启动”多个方法。这样一来,用户在使用时需要记忆很多名字,继承的优势就荡然无存了。为了解决这个问题,在面向对象的程序设计中引入了多态的机制。
多态是指一个程序中同名的不同方法共存的情况。主要通过子类对父类方法的覆盖来实现多态。这样一来,不同类的对象可以响应同名的方法来完成特定的功能,但其具体的实现方法却可以不同。例如同样的加法,把两个时间加在一起和把两个整数加在一起肯定完全不同。
通过方法覆盖,子类可以重新实现父类的某些方法,使其具有自己的特征。例如对于车类的加速方法,其子类(如赛车)中可能增加了一些新的部件来改善提高加速性能,这时可以在赛车类中覆盖父类的加速方法。覆盖隐藏了父类的方法,使子类拥有自己的具体实现,更进一步表明了与父类相比,子类所具有的特殊性。
多态性使语言具有灵活、抽象、行为共享的优势,很好地解决了应用程序函数同名问题。
二、实现的方法
多态需要通过继承来实现。
三、分类
1、编译多态(重载overload) eg、 函数重载
重载(overload):在同一个作用域(一般指一个类)的两个或多个方法函数名相同,参数列表不同的方法叫做重载,它们有三个特点(俗称两必须一可以):
- 方法名必须相同
- 参数列表必须不相同
- 返回值类型可以不相同
如:
1 public void Sleep()
2 {
3 Console.WriteLine("Animal睡觉");
4 }
5 public int Sleep(int time)
6 {
7 Console.WriteLine("Animal{0}点睡觉", time);
8 return time;
9 }
2、运行多态(重写override)
- 相同的方法名
- 相同的参数列表
- 相同的返回值。
1 如:父类中的定义:
2 public virtual void EatFood()
3 {
4 Console.WriteLine("Animal吃东西");
5 }
6
7 子类中的定义:
8
9 public override void EatFood()
10 {
11 Console.WriteLine("Cat吃东西");
12 //base.EatFood();
13 }
实现方式:
父类引用指向子类 例子:
1 namespace mydemo1
2 {
3 class Ren
4 {
5 public virtual void Speak()//
6 {
7 }
8 }
9
10 class Ghost
11 {
12 public void Eat(Ren e)
13 {
14 e.Speak();
15 Console.WriteLine("人类真好吃!");
16 }
17 }
18
19 class China : Ren
20 {
21 public override void Speak()
22 {
23 Console.WriteLine("汉语");
24 }
25 }
26
27 class Usa : Ren
28 {
29 public override void Speak()
30 {
31 Console.WriteLine("English");
32 }
33 }
34
35 class Program
36 {
37 public static void Main(string[] args)
38 {
39 Ren a = new China();//父类的引用指向子类的实例
40 Ren b = new Usa();
41
42 //a.Speak();
43 //b.Speak();
44
45 Ghost g = new Ghost();
46 China c = new China();
47 Usa u = new Usa();
48
49 Random r = new Random();
50 int a = r.Next(0, 3);
51
52 if (a == 1)
53 {
54 g.Eat(c);//改eat()方法传入的参数要求是Ren类的引用,可以向里面传其子类及父类的元素,子类对象代替父类对象
55 }
56 else
57 {
58 g.Eat(u);
59 }
60
61
62 }
63 }
64 }
3、虚方法
:即为基类中定义的允许在派生类中重写的方法,使用virtual关键字定义。
如:
1 public virtual void EatFood()
2 {
3 Console.WriteLine("Animal吃东西");
4 }
注意虚方法也可以直接调用
Animal a = new Animal();
a.EatFood();
运行结果:
4、抽象方法:在基类中定义的并且必须在派生类中重写的方法,使用abstract关键字定义。如:
public abstract class Biology
{
public abstract void Live();
}
public class Animal : Biology
{
public override void Live()
{
Console.WriteLine("Animal重写的抽象方法");
//throw new NotImplementedException();
}
}
注意:抽象方法只能在抽象类中定义,如果不在抽象类中定义,则会报出如下错误:
5、里氏代换原则和抽象依赖原则
里氏代换原则 如果某个方法接收的是父类引用,可以向里面传父类或其子类的元素,子类对象替代父类对象 例子:怪兽吃人
1 class Guaishou
2 {
3 public void Eat(Ren r)
4 {
5 r.Jiao();
6 Console.WriteLine("人类真好吃");
7 }
8 }
9
10 class Ren
11 {
12 public virtual void Speak()
13 {
14 Console.WriteLine("说话");
15 }
16
17 public virtual void Jiao()
18 {
19 Console.WriteLine("55555555555555555");
20 }
21
22 }
23
24 class American:Ren
25 {
26 public override void Speak()
27 {
28 Console.WriteLine("Hello");
29 }
30
31 public override void Jiao()
32 {
33 Console.WriteLine("SOS");
34 }
35 }
36
37 class Chinese:Ren
38 {
39 public override void Speak()
40 {
41 Console.WriteLine("你好");
42 }
43 public override void Jiao()
44 {
45 Console.WriteLine("救命");
46 }
47
48 }
49
50 class Program
51 {
52 static void Main(string[] args)
53 {
54 //Ren a = new Chinese();
55 //a.Speak();
56 //a = new American();
57 //a.Speak();
58
59 Guaishou g = new Guaishou();
60
61 //Ren ren;
62
63 Random r = new Random();
64 int a = r.Next(0,3);
65
66 if(a==1)
67 {
68 American ren= new American();
69 g.Eat(ren);
70 }
71 else
72 {
73 Chinese ren = new Chinese();
74 g.Eat(ren);
75 }
76
77
78 }
79 }
80 }
抽象依赖原则
用父类的引用来指向子类的实例 例子:运行多态的例子
6、隐藏方法:在派生类中定义的和基类中的某个方法同名的方法,使用new关键字定义。
如在基类Animal中有一方法Sleep():
public void Sleep() { Console.WriteLine("Animal Sleep"); }
new public void Sleep() { Console.WriteLine("Cat Sleep"); }
或者为:public new void Sleep() { Console.WriteLine("Cat Sleep"); }
注意:(1)隐藏方法不但可以隐藏基类中的虚方法,而且也可以隐藏基类中的非虚方法。
1 public abstract class Biology
2 {
3 public abstract void Live();
4 }
5 public class Animal : Biology
6 {
7 public override void Live()
8 {
9 Console.WriteLine("Animal重写的Live");
10 //throw new NotImplementedException();
11 }
12 public void Sleep()
13 {
14 Console.WriteLine("Animal Sleep");
15 }
16 public int Sleep(int time)
17 {
18 Console.WriteLine("Animal在{0}点Sleep", time);
19 return time;
20 }
21 public virtual void EatFood()
22 {
23 Console.WriteLine("Animal EatFood");
24 }
25 }
26 public class Cat : Animal
27 {
28 public override void EatFood()
29 {
30 Console.WriteLine("Cat EatFood");
31 //base.EatFood();
32 }
33 new public void Sleep()
34 {
35 Console.WriteLine("Cat Sleep");
36 }
37 //public new void Sleep()
38 //{
39 // Console.WriteLine("Cat Sleep");
40 //}
41 }
42 public class Dog : Animal
43 {
44 public override void EatFood()
45 {
46 Console.WriteLine("Dog EatFood");
47 //base.EatFood();
48 }
49 }
需要执行的代码:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //Animal的实例
6 Animal a = new Animal();
7 //Animal的实例,引用派生类Cat对象
8 Animal ac = new Cat();
9 //Animal的实例,引用派生类Dog对象
10 Animal ad = new Dog();
11 //Cat的实例
12 Cat c = new Cat();
13 //Dog的实例
14 Dog d = new Dog();
15 //重载
16 a.Sleep();
17 a.Sleep(23);
18 //重写和虚方法
19 a.EatFood();
20 ac.EatFood();
21 ad.EatFood();
22 //抽象方法
23 a.Live();
24 //隐藏方法
25 a.Sleep();
26 ac.Sleep();
27 c.Sleep();
28 Console.ReadKey();
29 }
30 }