[.net 面向对象编程基础] (13) 面向对象三大特性——多态

前面两节,我们了解了面向对象的的封装和继承特性,面向对象还有一大特性就是多态。比起前面的封装和继承,多态这个概念不是那么好理解。我们还是从一个事例开始:

公司最近为了陶冶情操,养了几种动物(Animal),有猫(Cat)、狗(Dog)、羊(Sheep),这些动物都有共同的特性,会吃(Eat)、会叫(Shout),但是它们吃的不同,叫的也不同。既然这样,我们能不能设计一个动物类(Animal)和它的成员(Eat方法、Shout方法)来表示这些动物的共同特征,而当我们关注猫时,猫来实现这两个成员(吃鱼、喵喵叫);当我们关注狗时,狗来实现这两个成员(吃肉和汪汪叫)。

1.什么是多态?

上述例子就是一个典型的多态,就是父类的一些成员,子类继承后去重写从而实现不同的功能。

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。这就是多态,这种特性称为多态性。

2.多态的分类

多态性分为两种,一种是编译时的多态性,一种是运行时的多态性。

编译时的多态性:编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。

运行时的多态性:运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中运行时的多态性是通过覆写虚成员实现。 

3.多态的实现

我们知道多态有两种,一种是编译时通过重载实现,另一种是运行时,通过重写或叫覆写来实现,那么如何实现他们?

3.1编译时多态:重载(overload)

重载(overload):重载指的是同一个类中有两个或多个名字相同但是参数不同的方法,(注:返回值不能区别函数是否重载),重载没有关键字。

注意:

A.从重载的定义来看,重载是一种编译时多态

B.重载不需要事先定义可重载的方法,即没有关键字

C.重载只是针对一个类内部的几个参数不同,名称相同的方法。

我们还在本节开篇那几只陶冶情操的动物来示例说明,代码如下:

 1 /// <summary>
 2 /// 狗(多态:重载事例)
 3 /// </summary>
 4 class Dog
 5 {
 6     /// <summary>
 7     /// 叫
 8     /// </summary>
 9     public void Shout()
10     {
11         Console.WriteLine("汪!");
12     }
13
14     /// <summary>
15     /// 叫(重载方法)
16     /// </summary>
17     public void ShoutCount(int count)
18     {
19         int i = 0;
20         string shout = "";
21         do
22         {
23             shout += "汪!";
24             i++;
25         } while (i <= count);
26         Console.WriteLine(shout);
27     }
28 }

//调用
Dog dog = new Dog();
dog.Shout();
dog.ShoutCount(5);

3.2运行时多态:重写

重写有两种,一种是override修饰符,另一种使用new修饰符,下面会举例说明两种重写的使用方法和异同。

重写(override):也称过载,重写是指子类对父类中虚函数或抽象函数的“覆盖”(这也就是有些书将过载翻译为覆盖的原因),但是这种“覆盖”和用new关键字来覆盖是有区别的。

下面以本节开题前例子,实现重写,代码如下:

  1     /// <summary>
  2     /// 动物类(父类)
  3     /// </summary>
  4     class Animal
  5     {
  6        /// <summary>
  7        /// 名字
  8        /// 说明:类和子类可访问
  9        /// </summary>
 10        protected string name;
 11
 12
 13         /// <summary>
 14         /// 构造函数
 15         /// </summary>
 16         /// <param name="name"></param>
 17         public Animal(string name)
 18         {
 19             this.name=name;
 20         }
 21
 22         /// <summary>
 23         /// 名字(虚属性)
 24         /// </summary>
 25         public virtual string MyName
 26         {
 27             get { return this.name; }
 28
 29         }
 30
 31         /// <summary>
 32         /// 吃(虚方法)
 33         /// </summary>
 34         public virtual void Eat()
 35         {
 36             Console.WriteLine("我会吃!");
 37         }
 38
 39         /// <summary>
 40         /// 叫(虚方法)
 41         /// </summary>
 42         public virtual void Shout()
 43         {
 44             Console.WriteLine("我会叫!");
 45         }
 46     }
 47
 48     /// <summary>
 49     /// 狗(子类)
 50     /// </summary>
 51     class Dog:Animal
 52     {
 53         string myName;
 54         public Dog(string name): base(name)
 55         {
 56             myName = name;
 57         }
 58
 59         /// <summary>
 60         /// 名字(重写父类属性)
 61         /// </summary>
 62         public override string MyName
 63         {
 64             get { return "我是:狗狗,我叫:"+this.name; }
 65
 66         }
 67
 68
 69         /// <summary>
 70         /// 吃(重写父类虚方法)
 71         /// </summary>
 72         public  override void Eat()
 73         {
 74             Console.WriteLine("我喜欢吃肉!");
 75         }
 76
 77         /// <summary>
 78         /// 叫(重写父类方法)
 79         /// </summary>
 80         public override void Shout()
 81         {
 82             Console.WriteLine("汪!汪!汪!");
 83         }
 84     }
 85     /// <summary>
 86     /// 猫(子类)
 87     /// </summary>
 88     class Cat : Animal
 89     {
 90         string myName;
 91         public Cat(string name)
 92             : base(name)
 93         {
 94             myName = name;
 95         }
 96         /// <summary>
 97         /// 名字(重写父类属性)
 98         /// </summary>
 99         public override string MyName
100         {
101             get { return "我是:猫咪,我叫:" + this.name; }
102
103         }
104
105         /// <summary>
106         /// 吃(重写父类虚方法)
107         /// </summary>
108         public override void Eat()
109         {
110             Console.WriteLine("我喜欢吃鱼!");
111         }
112
113         /// <summary>
114         /// 叫(重写父类方法)
115         /// </summary>
116         public override void Shout()
117         {
118             Console.WriteLine("喵!喵!喵!");
119         }
120     }
121
122     /// <summary>
123     /// 羊(子类)
124     /// </summary>
125     class Sheep : Animal
126     {
127         string myName;
128         public Sheep(string name)
129             : base(name)
130         {
131             myName = name;
132         }
133         /// <summary>
134         /// 名字(重写父类属性)
135         /// </summary>
136         public override string MyName
137         {
138             get { return "我是:羊羊,我叫:" + this.name; }
139
140         }
141
142         /// <summary>
143         /// 吃(重写父类虚方法)
144         /// </summary>
145         public override void Eat()
146         {
147             Console.WriteLine("我喜欢吃草!");
148         }
149
150         /// <summary>
151         /// 叫(重写父类方法)
152         /// </summary>
153         public override void Shout()
154         {
155             Console.WriteLine("咩!咩!咩!");
156         }
157     }

//调用方法
Animal dog = new Dog("旺财");
string myName=dog.MyName;
Console.WriteLine(myName);
dog.Eat();
dog.Shout();

//运行结果如下:
我是:狗狗,我叫:旺财
我喜欢吃肉!
汪!汪!汪!

//调用方法
Animal sheep = new  Sheep("美羊羊");
string myName = sheep.MyName;
Console.WriteLine(myName);
sheep.Eat();
sheep.Shout();

//运行结果如下:
我是:羊羊,我叫:美羊羊
我喜欢吃草!
咩!咩!咩!

重写(new)

new:覆盖指的是不同类中(基类或派生类)有两个或多个返回类型、方法名、参数都相同,但是方法体不同的方法。但是这种覆盖是一种表面上的覆盖,所以也叫隐藏,被覆盖的父类方法是可以调用得到的。

下面用实例说明,代码如下:

 1     /// <summary>
 2     /// 动物类(父类)
 3     /// </summary>
 4     class Animal
 5     {
 6        /// <summary>
 7        /// 名字
 8        /// 说明:类和子类可访问
 9        /// </summary>
10        protected string name;
11
12
13         /// <summary>
14         /// 构造函数
15         /// </summary>
16         /// <param name="name"></param>
17         public Animal(string name)
18         {
19             this.name=name;
20         }
21
22         /// <summary>
23         /// 名字(虚属性)
24         /// </summary>
25         public virtual string MyName
26         {
27             get { return this.name; }
28
29         }
30
31         /// <summary>
32         /// 吃(虚方法)
33         /// </summary>
34         public virtual void Eat()
35         {
36             Console.WriteLine("我会吃!");
37         }
38
39         /// <summary>
40         /// 叫(虚方法)
41         /// </summary>
42         public virtual void Shout()
43         {
44             Console.WriteLine("我会叫!");
45         }
46     }
47
48     /// <summary>
49     /// 狗(子类)
50     /// </summary>
51     class Dog:Animal
52     {
53         string myName;
54         public Dog(string name): base(name)
55         {
56             myName = name;
57         }
58         /// <summary>
59         /// 名字(重写父类属性)
60         /// </summary>
61         public override string MyName
62         {
63             get { return "我是:狗狗,我叫:"+this.name; }
64         }
65
66         /// <summary>
67         /// 吃(重写父类虚方法)
68         /// </summary>
69         new  public   void Eat()
70         {
71             Console.WriteLine("我喜欢吃肉!");
72         }
73
74         /// <summary>
75         /// 叫(重写父类方法)
76         /// </summary>
77         public new void Shout()
78         {
79             Console.WriteLine("汪!汪!汪!");
80         }
81     }

//调用方法
Animal dog = new Dog("旺财");
string myName=dog.MyName;
Console.WriteLine(myName);
dog.Eat();
dog.Shout();

//执行结果如下:
我是:狗狗,我叫:旺财
我会吃!
我会叫!

如下改一下调用方法:

//调用方法
Dog dog = new Dog("旺财");
string myName=dog.MyName;
Console.WriteLine(myName);
dog.Eat();
dog.Shout();

//执行结果如下:
我是:狗狗,我叫:旺财!
我爱吃肉!
汪!汪!汪!

可以看出,当派生类Dog的Eat()方法使用new修饰时,Dog的对象转换为Animal对象后,调用的是Animal类中的Eat()方法。其实可以理解为,使用new关键字后,使得Dog中的Eat()方法和Animal中的Eat()方法成为毫不相关的两个方法,只是它们的名字碰巧相同而已。所以, Animal类中的Eat()方法不管用还是不用virtual修饰,也不管访问权限如何,或者是没有,都不会对Dog的Eat()方法产生什么影响(只是因为使用了new关键字,如果Dog类没用从Animal类继承Eat()方法,编译器会输出警告)。

我想这是设计者有意这么设计的,因为有时候我们就是要达到这种效果。严格的说,不能说通过使用new来实现多态,只能说在某些特定的时候碰巧实现了多态的效果。

3.3 要点:

a.多态是面向对象的重要特性之一,指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

b.多态分为两种:一种是编译时多态,使用重载实现;另一种是运行时多态,使用重写实现;

c.重写有两种,一种使用override关键词,另一种使用new关键词

d.new重写实际上是对父类方法的隐藏,被覆盖的父类方法可以调用得到。因此new可以重写(或说是隐藏)的父类方法不一定要定义为虚方法或抽象方法。只是如果父类方法是虚方法或抽象方法时会覆盖父类方法,如果不是,则隐藏。

e.重载和覆盖的发生条件:
重载,必然发生在一个类中,函数名相同,参数类型或者顺序不同构成重载,与返回类型无关
重写,必然发生在基类和派生类中,其类函数用virtual修饰,派生类用override修饰
覆盖,在子类中写一个和基类一样名字(参数不同也算)的非虚函数,会让基类中的函数被隐藏,编译后会提示要求使用New关键字 new 修饰

隐藏,在子类中可以通过new 隐藏父类的方法

f.new覆盖与重写、重载的区别:

当子类与父类的参数不同时

当基类函数不是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载)

当基类函数是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载;因为参数不同,所以不是重写)

当子类与父类的参数相同时

当基类函数不是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载,因为基类不是虚函数,所以是隐藏不是重写)

当基类函数是虚函数时,基类函数将被覆盖。(因为子类和基类不在同一范围内,所以不是重载)

==============================================================================================

返回目录

 <如果对你有帮助,记得点一下推荐哦,有不明白的地方或写的不对的地方,请多交流>

==============================================================================================

转载于:https://www.cnblogs.com/yubinfeng/p/4557160.html

[.net 面向对象编程基础] (13) 面向对象三大特性——多态相关推荐

  1. [.net 面向对象编程基础] (18) 泛型

    [.net 面向对象编程基础] (18) 泛型 上一节我们说到了两种数据类型数组和集合,数组是指包含同一类型的多个元素,集合是指.net中提供数据存储和检索的专用类. 数组使用前需要先指定大小,并且检 ...

  2. [.net 面向对象编程基础] (20) LINQ使用

    [.net 面向对象编程基础] (20)  LINQ使用 通过上节LINQ的基础知识的学习,我们可以开始使用LINQ来进行内存数据的查询了,我们上节说了LINQ的定义为:Language Integr ...

  3. Day08 - 面向对象编程基础

    面向对象编程基础 活在当下的程序员应该都听过"面向对象编程"一词,也经常有人问能不能用一句话解释下什么是"面向对象编程",我们先来看看比较正式的说法. 把一组数 ...

  4. java 168转换成861_java实验-java语言面向对象编程基础

    java实验-java语言面向对象编程基础 (12页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 8.90 积分 广州大学学生实验报告广州大学学生实验报告 ...

  5. Java面向对象编程篇2——面向对象三大特点

    Java面向对象编程篇2--面向对象三大特点 1.封装 1.1.封装的概念 通常情况下可以在测试类给成员变量赋值一些合法但不合理的数值,无 论是编译阶段还是运行阶段都不会报错或者给出提示,此时与现实生 ...

  6. java面向对象程序_面向对象编程基础(java)

    面向对象编程基础 1.1 面向对象概述 在程序开发初期,大家使用的是结构化开发语言,也就是面向过程(opp),但随着市场需求剧增,软件的规模也越来越大,结构化语言的弊端也暴露出来. 开发周期无休止的拖 ...

  7. [.net 面向对象编程基础] (22) 事件

    [.net 面向对象编程基础] (22)  事件 事件(Event)是学习.net面向对象编程很重要的一部分,在学习事件之前,我们实际上已经在很多地方使用了事件,比如控件的click事件等,这些都是. ...

  8. java面向对象编程基础

    java面向对象编程基础 前言:什么是java 是咖啡飘香的清晨 - 是斯坦福校园意浓情深 - 是James的思想睿智 是剁手党双十一挥舞的利刃 是大数据云计算驰骋的平台 - 是ATM上吐出的钞票 - ...

  9. Python开发系列课程(9) - 面向对象编程基础

    面向对象编程基础 活在当下的程序员应该都听过"面向对象编程"一词,也经常有人问能不能用一句话解释下什么是"面向对象编程",我们先来看看比较正式的说法. 把一组数 ...

最新文章

  1. 1-4-RHEL6.3-用户及组管理(Red Hat Enterprise Linux Server release 6.3)@树袋飘零
  2. android 按钮 叠加,android - 叠加层按钮在Android 4.3中不起作用 - 堆栈内存溢出
  3. 两台服务器安装redis集群_Redis Cluster搭建高可用Redis服务器集群
  4. Jenkins+Github持续集成
  5. 威联通架设php网站_使用QNAP NAS虚拟主机功能架设多个网站
  6. 分享一个多线程实现[冒泡][选择][二分法]排序的例子
  7. SQLite学习手册(实例代码二)
  8. python 排序统计滤波器_马尔可夫链+贝叶斯滤波器的Python展示
  9. python实现贝叶斯分类器_python实现简单的朴素贝叶斯分类器
  10. jQuery年月日(生日)选择器
  11. 蜗牛导航网站模板+随机壁纸+天气插件
  12. php获取推特feed twitter timeline feed
  13. tar、tar.gz、tar.Z、tgz、bz2、bin软件包的安装
  14. WPF RichTextBox设置文本颜色
  15. quartus dcfifo操作
  16. 美团:团购突围后走向何方?
  17. jQuery图片特效脚本prettyPhoto简介
  18. “给在读研究生+未来要读研同学们的一封受益匪浅的信”(摘录+整合)
  19. 02论文分享与分析——基于ROS的移动机械臂底层规划及运动仿真
  20. mysql 默认值批量更改_mysql 批量修改默认值

热门文章

  1. 记录call、apply、bind的源码
  2. 转载:如何优雅的实现INotifyPropertyChanged接口
  3. JS 时间转化为几分钟前 几小时前 几天前
  4. Codeforces 补题记录
  5. java_内存的划分
  6. 误操作数据库的一个方法
  7. Entityframework Code First 系列之项目搭建
  8. SimpleTemplate模板引擎开发
  9. Linux 命令 查看监听端口
  10. angularjs中$http模块发送post请求request payload转form data