c# 面相对象4-多态性

一、定义:

       多态是面向对象程序设计的又一个特性。在面向过程的程序设计中,主要工作是编写一个个的过程或函数,这些过程和函数不能重名。例如在一个应用中,需要对数值型数据进行排序,还需要对字符型数据进行排序,虽然使用的排序方法相同,但要定义两个不同的过程(过程的名称也不同)来实现。

  在面向对象程序设计中,可以利用“重名”来提高程序的抽象度和简洁性。首先我们来理解实际的现象,例如,“启动”是所有交通工具都具有的操作,但是不同的具体交通工具,其“启动”操作的具体实现是不同的,如汽车的启动是“发动机点火——启动引擎”、“启动”轮船时要“起锚”、气球飞艇的“启动”是“充气——解缆”。如果不允许这些功能使用相同的名字,就必须分别定义“汽车启动”、“轮船启动”、“气球飞艇启动”多个方法。这样一来,用户在使用时需要记忆很多名字,继承的优势就荡然无存了。为了解决这个问题,在面向对象的程序设计中引入了多态的机制。

  多态是指一个程序中同名的不同方法共存的情况。主要通过子类对父类方法的覆盖来实现多态。这样一来,不同类的对象可以响应同名的方法来完成特定的功能,但其具体的实现方法却可以不同。例如同样的加法,把两个时间加在一起和把两个整数加在一起肯定完全不同。

  通过方法覆盖,子类可以重新实现父类的某些方法,使其具有自己的特征。例如对于车类的加速方法,其子类(如赛车)中可能增加了一些新的部件来改善提高加速性能,这时可以在赛车类中覆盖父类的加速方法。覆盖隐藏了父类的方法,使子类拥有自己的具体实现,更进一步表明了与父类相比,子类所具有的特殊性。

  多态性使语言具有灵活、抽象、行为共享的优势,很好地解决了应用程序函数同名问题。

二、实现的方法

      多态需要通过继承来实现。

三、分类

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)

子类中为满足自己的需要来重复定义某个方法的不同实现,需要用override关键字,被重写的方法必须是虚方法,用的是virtual关键字。它的特点是(三个相同):
  • 相同的方法名
  • 相同的参数列表
  • 相同的返回值。
     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 1  public virtual void EatFood()
    2 2         {
    3 3             Console.WriteLine("Animal吃东西");
    4 4         }

    注意虚方法也可以直接调用

    1  Animal a = new Animal();
    2             a.EatFood();

     运行结果:

  •  4、抽象方法:在基类中定义的并且必须在派生类中重写的方法,使用abstract关键字定义。如:
     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重写的抽象方法");
    10             //throw new NotImplementedException();
    11         } 
    12     }

     注意:抽象方法只能在抽象类中定义,如果不在抽象类中定义,则会报出如下错误: 

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():

1    public void Sleep()
2         {
3             Console.WriteLine("Animal Sleep");
4         }

     则在派生类Cat中定义隐藏方法的代码为:

1 new public void Sleep()
2         {
3             Console.WriteLine("Cat Sleep");
4         }

  或者为:

1   public new void Sleep()
2         {
3             Console.WriteLine("Cat Sleep");
4         }    

注意:(1)隐藏方法不但可以隐藏基类中的虚方法,而且也可以隐藏基类中的非虚方法。

           (2)隐藏方法中父类的实例调用父类的方法,子类的实例调用子类的方法。
           (3)和上一条对比:重写方法中子类的变量调用子类重写的方法,父类的变量要看这个父类引用的是子类的实例还是本身的实例,如果引用的是父类的实例那么调用基类的方法,如果引用的是派生类的实例则调用派生类的方法。
 
7、例题
 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     }

需要执行的代码:

class Program
    {
        static void Main(string[] args)
        {
            //Animal的实例
            Animal a = new Animal();
            //Animal的实例,引用派生类Cat对象
            Animal ac = new Cat();
            //Animal的实例,引用派生类Dog对象
            Animal ad = new Dog();
            //Cat的实例
            Cat c = new Cat();
            //Dog的实例
            Dog d = new Dog();
            //重载
            a.Sleep();
            a.Sleep(23);
            //重写和虚方法
            a.EatFood();
            ac.EatFood();
            ad.EatFood();
            //抽象方法
            a.Live();
            //隐藏方法
            a.Sleep();
            ac.Sleep();
            c.Sleep();
            Console.ReadKey();
        }
    }

 

 

posted @ 2015-08-28 08:53  陌钰陌城  Views(131)  Comments(0Edit  收藏  举报