多态(polymorphism)即一个名字具有多种语义。在面向对象中指一个方法可以有多种实现版本。类的多态表现为方法的多态,方法的多态主要有重载(overload)和覆盖(override)。
比如我们用下面的案例来展示一下我们不用多态的时候的劣势
Animal类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 多态的引入
{
class Animal
{
public void Eat()
{
Console.WriteLine(“动物在吃饭”);
}
}
}
Dog类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 多态的引入
{
class Dog:Animal
{
public new void Eat()
{
Console.WriteLine(“狗在吃骨头”);
}
}
}
Cat类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 多态的引入
{
class Cat:Animal
{
public new void Eat()
{
Console.WriteLine(“猫在吃鱼”);
}
}
}
Program类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 多态的引入
{
class Program
{
static void Main(string[] args)
{
Animal[] animals = new Animal[5];
//向上转型
animals[0] = new Dog();
animals[1] = new Dog();
animals[2] = new Cat();
animals[3] = new Cat();
animals[4] = new Dog();
for (int i = 0; i < animals.Length; i++)
{
if (animals[i] is Dog)
{
//向下转型
((Dog)animals[i]).Eat();
}
else
{
((Cat)animals[i]).Eat();
}
}
}
}
}
virtual和override实现的多态形式。
现在的话,只有两种动物 貌似看起来没有什么不方便的,但是如果要展示一个动物园的话,那么我们就得写一个庞大的分支语句结构
接下来我们使用virtual和override实现的多态形式。
Aniaml类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 多态的引入
{
class Animal
{
public virtual void Eat()
{
Console.WriteLine(“动物在吃饭”);
}
}
}
Dog类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 多态的引入
{
class Dog:Animal
{
public override void Eat()
{
Console.WriteLine(“狗在吃骨头”);
}
}
}
Cat类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 多态的引入
{
class Cat:Animal
{
public override void Eat()
{
Console.WriteLine(“猫在吃鱼”);
}
}
}
Program类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 多态的引入
{
class Program
{
static void Main(string[] args)
{
Animal[] animals = new Animal[5];
//向上转型
animals[0] = new Dog();
animals[1] = new Dog();
animals[2] = new Cat();
animals[3] = new Cat();
animals[4] = new Dog();
//先在只有两种动物 如果要展示一个动物园的话,那么我们就得写一个庞大的分支语句结构
for (int i = 0; i < animals.Length; i++)
{
//相对于上面的巨大的分支语句 这一行代码显得更加的清爽了
animals[i].Eat();
}
//有virtual 和override的重写 或 覆盖
//面试题:重写和重载有什么区别?
/*
* 相同:
* 重载和重写的方法名都一致
*
* 不同点:
* 重写是在父子类中的 重载是在同一个类中
* 重写的参数列表一致 重载的参数列表不同
* 重写是运行是多态 重载是编译时多态
*
*/
}
}
}
抽象类实现的多态
比如我们现在实现一个平面图形的面积和周长
AbstractTest类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 抽象类
{
//抽象类就是用abstract修饰的类
abstract class AbstractTest
{
private string name;
private int age;
//虚拟成员或者抽象成员不能是私有的
//抽象方法是用abstract修饰的方法
//抽象方法是不能还有方法体的
//抽象方法可以是public 和protected
//抽象类中可以写抽象方法
//抽象方法必须在抽象类中
//抽象类可以有实现的构造方法
public AbstractTest()
{
}
//抽象类不能有抽象的构造方法
//public abstract AbstractTest();
public abstract void Test();
//抽象类中可以写实例方法
public void Test01()
{
}
//因为抽象类中既有实现的方法又有未实现的方法 因此抽象类是一个不完全抽象的类
}
}
Test1 类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 抽象类
{
class Test1 : AbstractTest
{
public override void Test()
{
Console.WriteLine(“Test01”);
}
}
}
Test2 类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 抽象类
{
class Test2 : AbstractTest
{
public override void Test()
{
Console.WriteLine(“Test02”);
}
}
}
Program类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 抽象类
{
class Program
{
static void Main(string[] args)
{
//抽象类是无法进行实例化的
//AbstractTest test = new AbstractTest();
//只能通过里氏转换将子类向父类转化
AbstractTest test1 = new Test1();
test1.Test();
test1 = new Test2();
test1.Test();
}
}
}
接口实现的多态
对于有些功能,我们不能将其作为一个类去描述的时候,我们可以考虑使用接口来实现。比如我们说 “飞”这样一个功能,我们并不能抽象成一个父类。当然我们第一印象就是鸟类,但是作为鸟类,并不是所有的鸟类都会飞。比如说企鹅,如果我们描述飞机,风筝的时候。那么就更提不上鸟类了,但是我们可以将“飞”作为一个接口。
接口的定义
接口的命名规范,一般接口的名字都是以大写I开头,以后的每个单词的首字母要大写
例如:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 接口的认识
{
//接口修饰符是interface
interface IFly
{
//接口是一个没有方法体的方法
//接口是一个没有定义的方法
//接口是不需要访问修饰符的
void Fly();
}
}
继承自接口的类都必须实现接口中的抽象方法
ButterFly 类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 接口的认识
{
class ButterFly : IFly
{
public void Fly()
{
Console.WriteLine(“靠翅膀的振动和大气压飞起来”);
}
}
}
Kite 类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 接口的认识
{
class Kite : IFly
{
public void Fly()
{
Console.WriteLine(“大气压的阻力和牵引力”);
}
}
}
Plane 类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 接口的认识
{
class Plane : IFly
{
public void Fly()
{
Console.WriteLine(“大气压和引擎飞起来”);
}
}
}
Program类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 接口的认识
{
class Program
{
static void Main(string[] args)
{
//接口不可以被实例化
//IFly fly = new IFly();
IFly[] flys = new IFly[3];
flys[0] = new Kite();
flys[1] = new Plane();
flys[2] = new ButterFly();
//证明了接口可以实现面向对象的多态
for (int i = 0; i < flys.Length; i++)
{
flys[i].Fly();
}
}
}
}
显示实现接口
C#是一种多继承和单继承都存在的语言。只不过单继承针对的是类,多继承针对的是接口。
显示实现接口解决的问题是同一个类继承了多个包含具有多个相同方法名的接口发生冲突的问题
ITest01接口:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 显示实现接口
{
interface ITest01
{
int Add(int num1, int num2);
}
}
ITest02接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 显示实现接口
{
interface ITest02
{
int Add(int num1,int num2);
}
}
Test 类结成了两个接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 显示实现接口
{
class Test : ITest01, ITest02
{
//显示实现接口在方法名前交了所属接口的名字,便于区分 当前的方法属于哪个接口
int ITest01.Add(int num1, int num2)
{
return num1 + num2;
}
int ITest02.Add(int num1, int num2)
{
return num1 * num2;
}
}
}
实现多态的三种方式的区别
•第一种方式:
virtual和override的结合构成重写父类的方法
使用场景:对待多个类,可以找到父类,父类对某一个行为也有具体实现的场景
•第二种方式:
abstract和override的构成重写重写方法
使用场景:对待多个类,可以找到父类,但是父类对该行为不能做出具体的实现
•第三种方式:
interface接口
使用场景:对待多个类,找不到共同的父类,但是可以找到共同的行为(方法或函数),那么我们可以把该行为封装成一个接口来使用
抽象类和接口的区别
•相同点:
抽象类和接口都不能实例化
抽象类中的抽象方法和接口中的方法都没有方法体
使用抽象类和接口都可以实现面向对象中的多态
•不同点:
抽象类是不完全抽象,接口是完全抽象
抽象类中可以有非抽象方法,接口中的方法都是抽象方法
抽象类中的抽象方法可以含有访问修饰符,接口中的方法不含有访问修饰符