[.net 面向对象编程基础] (13) 面向对象三大特性——多态
[.net 面向对象编程基础] (13) 面向对象三大特性——多态
前面两节,我们了解了面向对象的的封装和继承特性,面向对象还有一大特性就是多态。比起前面的封装和继承,多态这个概念不是那么好理解。我们还是从一个事例开始:
公司最近为了陶冶情操,养了几种动物(Animal),有猫(Cat)、狗(Dog)、羊(Sheep),这些动物都有共同的特性,会吃(Eat)、会叫(Shout),但是它们吃的不同,叫的也不同。既然这样,我们能不能设计一个动物类(Animal)和它的成员(Eat方法、Shout方法)来表示这些动物的共同特征,而当我们关注猫时,猫来实现这两个成员(吃鱼、喵喵叫);当我们关注狗时,狗来实现这两个成员(吃肉和汪汪叫)。
1.什么是多态?
上述例子就是一个典型的多态,就是父类的一些成员,子类继承后去重写从而实现不同的功能。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。这就是多态,这种特性称为多态性。
2.多态的分类
多态性分为两种,一种是编译时的多态性,一种是运行时的多态性。
编译时的多态性:编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。
运行时的多态性:运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中运行时的多态性是通过覆写虚成员实现。
3.多态的实现
我们知道多态有两种,一种是编译时通过重载实现,另一种是运行时,通过重写或叫覆写来实现,那么如何实现他们?
3.1编译时多态:重载(overload)
重载(overload):重载指的是同一个类中有两个或多个名字相同但是参数不同的方法,(注:返回值不能区别函数是否重载),重载没有关键字。
注意:
A.从重载的定义来看,重载是一种编译时多态
B.重载不需要事先定义可重载的方法,即没有关键字
C.重载只是针对一个类内部的几个参数不同,名称相同的方法。
我们还在本节开篇那几只陶冶情操的动物来示例说明,代码如下:
/// <summary>
/// 狗(多态:重载事例)
/// </summary>
class Dog
{
/// <summary>
/// 叫
/// </summary>
public void Shout()
{
Console.WriteLine("汪!");
} /// <summary>
/// 叫(重载方法)
/// </summary>
public void ShoutCount(int count)
{
int i = ;
string shout = "";
do
{
shout += "汪!";
i++;
} while (i <= count);
Console.WriteLine(shout);
}
}
//调用
Dog dog = new Dog();
dog.Shout();
dog.ShoutCount();
3.2运行时多态:重写
重写有两种,一种是override修饰符,另一种使用new修饰符,下面会举例说明两种重写的使用方法和异同。
重写(override):也称过载,重写是指子类对父类中虚函数或抽象函数的“覆盖”(这也就是有些书将过载翻译为覆盖的原因),但是这种“覆盖”和用new关键字来覆盖是有区别的。
下面以本节开题前例子,实现重写,代码如下:
/// <summary>
/// 动物类(父类)
/// </summary>
class Animal
{
/// <summary>
/// 名字
/// 说明:类和子类可访问
/// </summary>
protected string name; /// <summary>
/// 构造函数
/// </summary>
/// <param name="name"></param>
public Animal(string name)
{
this.name=name;
} /// <summary>
/// 名字(虚属性)
/// </summary>
public virtual string MyName
{
get { return this.name; } } /// <summary>
/// 吃(虚方法)
/// </summary>
public virtual void Eat()
{
Console.WriteLine("我会吃!");
} /// <summary>
/// 叫(虚方法)
/// </summary>
public virtual void Shout()
{
Console.WriteLine("我会叫!");
}
} /// <summary>
/// 狗(子类)
/// </summary>
class Dog:Animal
{
string myName;
public Dog(string name): base(name)
{
myName = name;
} /// <summary>
/// 名字(重写父类属性)
/// </summary>
public override string MyName
{
get { return "我是:狗狗,我叫:"+this.name; } } /// <summary>
/// 吃(重写父类虚方法)
/// </summary>
public override void Eat()
{
Console.WriteLine("我喜欢吃肉!");
} /// <summary>
/// 叫(重写父类方法)
/// </summary>
public override void Shout()
{
Console.WriteLine("汪!汪!汪!");
}
}
/// <summary>
/// 猫(子类)
/// </summary>
class Cat : Animal
{
string myName;
public Cat(string name)
: base(name)
{
myName = name;
}
/// <summary>
/// 名字(重写父类属性)
/// </summary>
public override string MyName
{
get { return "我是:猫咪,我叫:" + this.name; } } /// <summary>
/// 吃(重写父类虚方法)
/// </summary>
public override void Eat()
{
Console.WriteLine("我喜欢吃鱼!");
} /// <summary>
/// 叫(重写父类方法)
/// </summary>
public override void Shout()
{
Console.WriteLine("喵!喵!喵!");
}
} /// <summary>
/// 羊(子类)
/// </summary>
class Sheep : Animal
{
string myName;
public Sheep(string name)
: base(name)
{
myName = name;
}
/// <summary>
/// 名字(重写父类属性)
/// </summary>
public override string MyName
{
get { return "我是:羊羊,我叫:" + this.name; } } /// <summary>
/// 吃(重写父类虚方法)
/// </summary>
public override void Eat()
{
Console.WriteLine("我喜欢吃草!");
} /// <summary>
/// 叫(重写父类方法)
/// </summary>
public override void Shout()
{
Console.WriteLine("咩!咩!咩!");
}
}
//调用方法
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:覆盖指的是不同类中(基类或派生类)有两个或多个返回类型、方法名、参数都相同,但是方法体不同的方法。但是这种覆盖是一种表面上的覆盖,所以也叫隐藏,被覆盖的父类方法是可以调用得到的。
下面用实例说明,代码如下:
/// <summary>
/// 动物类(父类)
/// </summary>
class Animal
{
/// <summary>
/// 名字
/// 说明:类和子类可访问
/// </summary>
protected string name; /// <summary>
/// 构造函数
/// </summary>
/// <param name="name"></param>
public Animal(string name)
{
this.name=name;
} /// <summary>
/// 名字(虚属性)
/// </summary>
public virtual string MyName
{
get { return this.name; } } /// <summary>
/// 吃(虚方法)
/// </summary>
public virtual void Eat()
{
Console.WriteLine("我会吃!");
} /// <summary>
/// 叫(虚方法)
/// </summary>
public virtual void Shout()
{
Console.WriteLine("我会叫!");
}
} /// <summary>
/// 狗(子类)
/// </summary>
class Dog:Animal
{
string myName;
public Dog(string name): base(name)
{
myName = name;
}
/// <summary>
/// 名字(重写父类属性)
/// </summary>
public override string MyName
{
get { return "我是:狗狗,我叫:"+this.name; }
} /// <summary>
/// 吃(重写父类虚方法)
/// </summary>
new public void Eat()
{
Console.WriteLine("我喜欢吃肉!");
} /// <summary>
/// 叫(重写父类方法)
/// </summary>
public new void Shout()
{
Console.WriteLine("汪!汪!汪!");
}
}
//调用方法
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覆盖与重写、重载的区别:
当子类与父类的参数不同时
当基类函数不是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载)
当基类函数是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载;因为参数不同,所以不是重写)
当子类与父类的参数相同时
当基类函数不是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载,因为基类不是虚函数,所以是隐藏不是重写)
当基类函数是虚函数时,基类函数将被覆盖。(因为子类和基类不在同一范围内,所以不是重载)
==============================================================================================
返回目录 <如果对你有帮助,记得点一下推荐哦,有不明白的地方或写的不对的地方,请多交流>
==============================================================================================
[.net 面向对象编程基础] (13) 面向对象三大特性——多态的更多相关文章
- [.net 面向对象编程基础] (11) 面向对象三大特性——封装
[.net 面向对象编程基础] (11) 面向对象三大特性——封装 我们的课题是面向对象编程,前面主要介绍了面向对象的基础知识,而从这里开始才是面向对象的核心部分,即 面向对象的三大特性:封装.继承. ...
- [.net 面向对象编程基础] (12) 面向对象三大特性——继承
[.net 面向对象编程基础] (12) 面向对象三大特性——继承 上节我们说了面向对象的三大特性之一的封装,解决了将对同一对象所能操作的所有信息放在一起,实现统一对外调用,实现了同一对象的复用,降低 ...
- [.net 面向对象编程基础] (1) 开篇
[.net 面向对象编程基础] (1)开篇 使用.net进行面向对象编程也有好长一段时间了,整天都忙于赶项目,完成项目任务之中.最近偶有闲暇,看了项目组中的同学写的代码,感慨颇深.感觉除了定义个类,就 ...
- [.net 面向对象编程基础] (14) 重构
[.net 面向对象编程基础] (14) 重构 通过面向对象三大特性:封装.继承.多态的学习,可以说我们已经掌握了面向对象的核心.接下来的学习就是如何让我们的代码更优雅.更高效.更易读.更易维护.当然 ...
- [.net 面向对象编程基础] (17) 数组与集合
[.net 面向对象编程基础] (17) 数组与集合 学习了前面的C#三大特性,及接口,抽象类这些相对抽象的东西以后,是不是有点很累的感觉.具体的东西总是容易理解,因此我们在介绍前面抽象概念的时候,总 ...
- [.net 面向对象编程基础] (20) LINQ使用
[.net 面向对象编程基础] (20) LINQ使用 通过上节LINQ的基础知识的学习,我们可以开始使用LINQ来进行内存数据的查询了,我们上节说了LINQ的定义为:Language Integr ...
- 大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础
第五章 函数式编程-基础5.1 函数式编程内容说明5.1.1 函数式编程内容5.1.2 函数式编程授课顺序5.2 函数式编程介绍5.2.1 几个概念的说明5.2.2 方法.函数.函数式编程和面向对象编 ...
- [.net 面向对象编程基础] (2) 关于面向对象编程
[.net 面向对象编程基础] (2) 关于面向对象编程 首先是,面向对象编程英文 Object-Oriented Programming 简称 OOP 通俗来说,就是 针对对象编程的意思 那么问 ...
- [.net 面向对象编程基础] (3) 基础中的基础——数据类型
[.net 面向对象编程基础] (3) 基础中的基础——数据类型 关于数据类型,这是基础中的基础. 基础..基础..基础.基本功必须要扎实. 首先,从使用电脑开始,再到编程,电脑要存储数据,就要按类型 ...
随机推荐
- PHP中spl_autoload_register函数的用法
spl_autoload_register (PHP 5 >= 5.1.2) spl_autoload_register — 注册__autoload()函数 说明bool spl_autolo ...
- Daily Scrum 12.17
今日完成任务: 解决匿名回答时候字数限制问题:完善了用户界面的UI设计: 明日任务: 孙思权 接口设计 晏旭瑞 资源索引问题 冯飘飘 解决上传资源时候不勾选或不添加选项内容可以上传的问题 黎柱金 进行 ...
- Beyond Compare 2
Beyond Compare 2 确实很好用,差异行不交叉,自动留出空白,比windiff要清楚.
- 14.Xcode8imageview图片圆角不显示的bug
第一种方法: 在imageView调用clipToBounds的之前,[super awakeFromNib]之后,加上[self.contentView layoutIfNeeded];就可以了,反 ...
- jQuery学习-css、class操作、动画方法的运用、jQ操作Dom节点
css操作(设置单个/多个样式.获取样式) //修改单个属性:括号之中直接是需要修改的样式名,值 css(name,value) //例:$("#one").css("b ...
- load与initialize
NSObject类有两种初始化方式load和initialize load + (void)load; 对于加入运行期系统的类及分类,必定会调用此方法,且仅调用一次. iOS会在应用程序启动的时候调用 ...
- sharepoint 网站创建
打开开始菜单,右键sharepoint管理中心以管理员身份打开 打开管理web应用程序菜单,并新建web应用程序 新建web应用程序配置,大多数采用默认配置. 建议自定义端口号,URL的端口号和设置的 ...
- 我与solr(四)--solrJ
SolrJ索引库: solr提供的一个客户端操作框架,在文件/solr6.2/dist下面可以找到该jar包solrj.jar以及相关jar包,可以使用maven添加. java使用solrJ如下: ...
- Microsoft.Office.Interop.Excel 程序集引用 ,Microsoft.Office.Interop.Excel.ApplicationClass 无法嵌入互操作类型
using Microsoft.Office.Interop.Excel 添加程序集引用 方法:在引用--程序集--扩展中,添加引用Microsoft.Office.Interop.Excel,此 ...
- localStorage, localforage, web sql三者的比较
最近的项目中用到了前端存储,最初选用的是localStorage,这个是html5里面新增的API,用法很简单.setItem getItem clear. 值得注意的是,localStorage中存 ...