比起前面的封装和继承,多态这个概念不是那么好理解。我们还是从一个事例开始:

公司最近为了陶冶情操,养了几种动物(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 Shout(int count)
{
int i = ;
string shout = "";
do
{
shout += "汪!";
i++;
} while (i <= count);
Console.WriteLine(shout);
}
}
/调用
Dog dog = new Dog();
dog.Shout();
dog.Shout();

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("汪!汪!汪!");
}
}
//调用方法
Animal dog = new Dog("旺财");
string myName=dog.MyName;
Console.WriteLine(myName);
dog.Eat();
dog.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("汪!汪!汪!");
}
}
//调用方法 使用new重写,则只调用父类的方法
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覆盖与重写、重载的区别:

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

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

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

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

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

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

C#面向对象三大特性之三:多态的更多相关文章

  1. python 面向对象三大特性(封装 多态 继承)

    今天我们来学习一种新的编程方式:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)注:Java和C#来说只支持面向对象编程,而python比较灵活即支持面 ...

  2. Java中面向对象三大特性之——多态

    多态的概述:  多态是继封装.继承之后,面向对象的第三大特性. 生活中,比如跑的动作,小猫.小狗和大象,跑起来是不一样的.再比如飞的动作,昆虫.鸟类和飞机,飞起来也是不一样的.可见,同一行为,通过不同 ...

  3. JAVA三大特性之三——多态

    作为JAVA的三大特性之一,多态性是很多人都没有弄清楚的一个重要特性,今天我就来从我所理解的角度来说一下. 首先,从他的字面意思来理解,多态,从其字面来理解就是多种形态,多种表现形式.根据这些,我最能 ...

  4. python基础(25):面向对象三大特性二(多态、封装)

    1. 多态 1.1 什么是多态 多态指的是一类事物有多种形态. 动物有多种形态:人,狗,猪. import abc class Animal(metaclass=abc.ABCMeta): #同一类事 ...

  5. 《Python》 面向对象三大特性之多态、封装

    一.多态 1.什么是多态? 一个类表现出的多种状态:通过继承来实现的 在Java中的表现:在一个函数中需要给参数指定数据类型,如果这个地方可以接收两个以上类型的参数,那么这些类型应该有一个父类,这个父 ...

  6. C#面向对象三大特性:多态

    什么是多态 公司最近为了陶冶情操,养了几种动物(Animal),有猫(Cat).狗(Dog).羊(Sheep),这些动物都有共同的特性,会吃(Eat).会叫(Shout),但是它们吃的不同,叫的也不同 ...

  7. 面向对象三大特性一一多态(polymorphism)

    package com.bjsxt.oop.polymorphism; public class Animal { public void voice(){ System.out.println(&q ...

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

    [.net 面向对象编程基础] (13) 面向对象三大特性——多态 前面两节,我们了解了面向对象的的封装和继承特性,面向对象还有一大特性就是多态.比起前面的封装和继承,多态这个概念不是那么好理解.我们 ...

  9. JAVA基础——面向对象三大特性:封装、继承、多态

    JAVA面向对象三大特性详解 一.封装 1.概念: 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问. 2.好处: 只能通过规定的方法访问数据. ...

随机推荐

  1. Centos安装ntfs

    ntfs优盘插在Linux上是无法直接使用的,需要安装ntfs插件才可使用 centos上安装ntfs-3g 下载ntfs-3g安装包,上传至需要安装的服务器并解压 cd 进入ntfs-3g目录,依次 ...

  2. 亚马逊EC2服务器登录方法

    1.根据官网提供的方法登录连接到EC2服务器(官网推荐windows用户使用PUTTY连接) 2. 创建root的密码,输入如下命令: sudo passwd root 3.然后会提示你输入new p ...

  3. ajax实现聊天室功能

    需求如下: 先死后活. 需求分析,分析思路如图所示: 1.创建数据库 create database chat; create table messages( id int unsigned prim ...

  4. 十八 Django框架,生成二维码

    用Python来生成二维码,需要qrcode模块,qrcode模块依赖Image 模块,所以首先安装这两个模块 生成二维码保存图片在本地 import qrcode img = qrcode.make ...

  5. hibernate复习第(二)天

    今日要点: 关联映射 多对一(Employee - Department) 一对多(Department - Employee) 一对一(Person - IdCard) 多对多(teachet - ...

  6. Android之ExpandableList扩展用法(基于BaseExpandableListAdapter)

    1.简介 基于基于BaseExpandableListAdapter扩展的ExpandableList用法,现在网上流行的主要有两种:第一种是向BaseExpandableListAdapter传入两 ...

  7. redis学习--Hashes数据类型

    本文转自:http://www.cnblogs.com/stephen-liu74/archive/2012/03/19/2352932.html 一.概述: 我们可以将Redis中的Hashes类型 ...

  8. ACM学习历程—广东工业大学2016校赛决赛-网络赛C wintermelon的魔界寻路之旅(最短路 && 递推)

    题目链接:http://gdutcode.sinaapp.com/problem.php?cid=1031&pid=2 题目由于要找对称的路径,那么狠明显可以把右下角的每一块加到左上角对应的每 ...

  9. bzoj 2850 巧克力王国 —— K-D树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2850 只要暴力判断是否全选一个子树或全不选,如果都不是就进入查询: 要注意值有负,所以不是直 ...

  10. keepalive安装和配置

    1.下载安装包并解压 sudo wget http://www.keepalived.org/software/keepalived-1.2.13.tar.gz tar zxvf keepalived ...