概念
让一个对象能够表现出多种的状态(类型)
实现多态的手段
1、虚方法 2、抽象类 3、接口
虚方法(类似Java中的重写)
步骤:
将父类的方法标记为虚方法 ,使用关键字 virtual,这个函数可以被子类重新写一个遍。
下例代码中,想要调用子类中的Eat(),发现并不可以,调用的是父类中的Eat()。
namespace Demo {
class Animal {
public void Eat() {
Console.WriteLine("吃");
}
}
class Cat : Animal {
public void Eat() {
Console.WriteLine("吃鱼");
}
}
class Dog : Animal {
public void Eat() {
Console.WriteLine("吃骨头");
}
}
class Program {
static void Main(string[] args) {
Animal c = new Cat();
c.Eat();
Animal d = new Dog();
d.Eat();
Console.ReadKey();
}
}
}
运行结果:
想要实现调用子类中的方法就需要两个关键字
virtual和override
在父类的方法中使用virtual进行修饰
在子类的方法中使用override进行修饰
namespace Demo {
class Animal {
public virtual void Eat() {
Console.WriteLine("吃");
}
}
class Cat : Animal {
public override void Eat() {
Console.WriteLine("吃鱼");
}
}
class Dog : Animal {
public override void Eat() {
Console.WriteLine("吃骨头");
}
}
class Program {
static void Main(string[] args) {
Animal c = new Cat();
c.Eat();
Animal d = new Dog();
d.Eat();
Animal a = new Animal();
a.Eat();
Console.ReadKey();
}
}
}
运行结果:
抽象类
下例代码中,Animal被abstract修饰,是一个抽象类。它的Brak()被abstract修饰,是个抽象方法,抽象方法没有方法体。
namespace Demo {
abstract class Animal {
public abstract void Bark();
public void Eat() {
Console.WriteLine("吃");
}
}
class Cat : Animal {
public override void Bark() {
Console.WriteLine("喵喵");
}
}
class Dog : Animal {
public override void Bark() {
Console.WriteLine("汪汪");
}
}
class Program {
static void Main(string[] args) {
Animal c = new Cat();
c.Bark();
Animal d = new Dog();
d.Bark();
Console.ReadKey();
}
}
}
运行结果
抽象类的特点
1.抽象成员必须标记为abstract,并且不能有任何实现。
2.抽象成员必须在抽象类中。
3.抽象类不能被实例化
4.子类继承抽象类后,必须把父类中的所有抽象成员都重写。
(除非子类也是一个抽象类,则可以不重写)
5.抽象成员的访问修饰符不能是private
6.在抽象类中可以包含实例成员。
并且抽象类的实例成员可以不被子类实现
7.抽象类是有构造函数的。虽然不能被实例化。
8、如果父类的抽象方法中有参数,那么。继承这个抽象父类的子类在重写父类的方法的时候必须传入对应的参数。
如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候 也必须要传入返回值。
======
如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法来实现多态。
如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。
接口
接口是指定一组方法成员而不实现它们的引用类型。接口其实就是一个规范、标准。
public interface IFlyable
{
//接口中的成员不允许添加访问修饰符 ,默认就是public。类中成员不添加访问修饰符,默认访问级别是private
void Fly();
string Test();
//自动属性
string Name
{
get;
set;
}
}
接口是一种规范。
只要一个类继承了一个接口,这个类就必须实现这个接口中所有的成员
为了多态。
接口不能被实例化。
也就是说,接口不能new(不能创建对象)
接口中的成员不能加“访问修饰符”,接口中的成员访问修饰符为public,不能修改。
(默认为public)
接口中的成员不能有任何实现(“光说不做”,只是定义了一组未实现的成员)。
接口中只能有方法、属性、索引器、事件,不能有“字段”和构造函数。
接口与接口之间可以继承,并且可以多继承。
接口并不能去继承一个类,而类可以继承接口 (接口只能继承于接口,而类既可以继承接口,也可以继承类)
实现接口的子类必须实现该接口的全部成员。
一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须写在IA的前面。
class MyClass:A,IA{},因为类是单继承的。
显式实现接口的目的:解决方法的重名问题
什么时候显示的去实现接口:
当继承的借口中的方法和参数一摸一样的时候,要是用显示的实现接口
当一个抽象类实现接口的时候,需要子类去实现接口。
显式实现接口:
namespace Demo {
class Program {
static void Main(string[] args) {
//显式实现接口就是为了解决方法的重名问题
IFlyable fly = new Bird();
fly.Fly();
Bird bird = new Bird();
bird.Fly();
Console.ReadKey();
}
}
public class Bird : IFlyable {
public void Fly() {
Console.WriteLine("鸟飞会");
}
/// <summary>
/// 显示实现接口
/// </summary>
void IFlyable.Fly() {
Console.WriteLine("我是接口的飞");
}
}
public interface IFlyable {
void Fly();
}
}
运行结果:
在最新的C#版本中,接口进行了变动,具体参考官方文档