多态,其实就是一个事物的不同表示方法.
多态的作用:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
比如:
Person→Chinese→American→Korean,每个国家的人都有一个说出自己国籍的方法,但这个事情就是说出自己的国籍,只是说出的方法不一样.
当有一个Person[]的时候,可以循环实现每个国家的人。也就是调用每一个子类的实现方法.
实现多态的主要手段有三种:
1.虚方法virtual
2.抽象方法abstract
3.接口 interface
下面详细总结三种方法的具体使用.
一,虚方法 Virtual
虚方法可以给父类中的方法一个实现,比如ToString()方法
虚方法必须有实现部分,哪怕是空实现。
案例:员工类、部门经理类(部门经理也是员工,所以要继承自员工类。员工有上班打卡的方法。用类来模拟。
本例子代码如下:
- public class Person
- {
- public Person(string name)
- {
- this.Name = name;
- }
- public string Name
- {
- get;
- set;
- }
- //把普通方法变为虚方法
- //当一个方法是虚方法的时候,那么这个方法在子类中就可以重写了.
- //子类继承父类以后,可以直接使用该方法,也可以将该方法重写
- public virtual void Show()
- {
- Console.WriteLine("父类中的show方法");
- }
- }
- /// <summary>
- /// 中国人类
- /// </summary>
- public class Chinese : Person
- {
- public Chinese(string name)
- : base(name)
- {
- }
- public override void Show()
- {
- Console.WriteLine("我叫:{0},我是中国人!", Name);
- }
- }
- /// <summary>
- /// 日本人类
- /// </summary>
- public class Japanese : Person
- {
- public Japanese(string name)
- : base(name)
- {
- }
- public override void Show()
- {
- Console.WriteLine("我叫:{0},我是日本人!", Name);
- }
- }
- /// <summary>
- /// 美国人类
- /// </summary>
- public class American : Person
- {
- public American(string name)
- : base(name)
- {
- }
- public override void Show()
- {
- Console.WriteLine("我叫:{0},我是美国人", Name);
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Chinese cn1 = new Chinese("杨中科");
- Chinese cn2 = new Chinese("韩超");
- Japanese jp1 = new Japanese("村上春树");
- Japanese jp2 = new Japanese("安倍");
- American us1 = new American("Chris");
- //对扩展开放,对修改关闭-----开放封闭原则.
- Person[] cns = new Person[] { cn1, cn2, jp1, jp2, us1 };
- //遍历数组中的
- for (int i = 0; i < cns.Length; i++)
- {
- //判断当前的cns[i]中存储的对象是否是chinese类型
- cns[i].Show();
- }
- //所有的类的ToString()方法都是集成自Object类,返回的值应该是 命名空间.类名
- //如果不是这个得话,说明对ToString()这个方法重写啦~
- Console.WriteLine(new MyClass().ToString());
- Console.ReadKey();
- }
- }
1.父类中如果有方法需要让子类重写,则可以将该方法标记为virtual
2.虚方法在父类中必须有实现,哪怕是空实现。
3.虚方法子类可以重写(override),也可以不重写。
二、抽象类实现多态 abstract
什么是抽象类?通俗的说就是,光说不做的类,就是不能被实例化.
首先,抽象类不能被实例化,抽象类中可以有普通成员。(这一点可以充分实现类的自我扩展)
抽象类存在的意义:
1.抽象类不能被实例化,只能被其他类继承
2.继承抽象类的子类必须把抽象类中的所有抽象成员都重写(实现)(除非子类也是抽象类。)
3.抽象类就是为了重写→多态(代码重用)。
4.抽象类中可以有实例成员也可以有抽象成员。
下面举一个例子:
- class Program
- {
- //橡皮鸭子(RubberDuck)、真实的鸭子(RealDuck)。两个鸭子都会游泳,而橡皮鸭子和真实的鸭子都会叫,只是叫声不一样,橡皮鸭子“唧唧”叫,真实地鸭子“嘎嘎”叫
- static void Main(string[] args)
- {
- Duck duck = new RubberDuck();
- duck.Bark();
- duck.Swim();
- Console.ReadKey();
- }
- }
- public abstract class Duck
- {
- public void Swim()
- {
- Console.WriteLine("鸭子会游泳");
- }
- public abstract void Bark();
- }
- //真实鸭子
- public class RealDuck : Duck
- {
- public override void Bark()
- {
- Console.WriteLine("真实鸭子 嘎嘎 叫!");
- }
- }
- //橡皮鸭子
- public class RubberDuck : Duck
- {
- public override void Bark()
- {
- Console.WriteLine("橡皮鸭子 唧唧叫!");
- }
- }
1.父类中如果有方法需要让子类重写,则可以将该方法标记为virtual
2.虚方法在父类中必须有实现,哪怕是空实现。
3.虚方法子类可以重写(override),也可以不重写。
虚方法和抽象方法的区别。
虚方法必须有实现,抽象方法必须没有实现
抽象方法必须在抽象类中声明,虚方法可以出现在抽象类中
抽象方法必须在子类中重写,虚方法可以被重写
三、接口实现多态
什么是接口?
接口就是一种规范,协议(*),约定好遵守某种规范就可以写通用的代码。
定义了一组具有各种功能的方法。(只是一种能力,没有具体实现,像抽象方法一样,“光说不做”)
可以把法律理解为一个接口(一种规范)
任何人想要在中国生活,必须遵守中国的法律,也就是说任何人要想在中国,就必须实现法律这个接口。(实现接口的意思就是遵守了接口中的规范。)
皮包公司(接口)。你给我活我有能力去开发网站,但是接到活以后我公司没人会做。于是我把他交给有能力去做的人(实现功能的类)
使用接口来实现对扩展开放,对修改关闭
接口定义一种能力
子类继承抽象类,实现接口
接口中的成员必须不能有实现
接口中的成员不能有访问修饰符,隐式公开
接口中可以有属性、方法、索引器等,但不能有字段
接口中的所有程序必须被子类中全部实现
接口存在的意义:多态。多态的意义:程序可扩展性。最终→节省成本,提高效率。
接口解决了类的多继承的问题
接口解决了类继承以后体积庞大的问题。
接口之间可以实现多继承
接口注意事项:
1.接口中只能包含方法。(方法、属性、索引器、事件)。
2.接口中的方法也不能有任何实现(就像抽象方法一样,直接加个分号)。
3.接口中的成员不能有任何访问修饰符(哪怕是public),默认为public。
4.接口也不能被实例化。
为什么使用接口??
当多个类型,都具有某个或某几个功能时(方法),但是这几个类型,又分属于不同的系列(这几个类型没有共同的父类,这时就不能用抽象类了。),所以这时,为了实现多态就可以考虑把这几个类型共有的方法提取到一个接口中,让这几个类型分别实现该接口。
接口代码示例:
- class Program
- {
- static void Main(string[] args)
- {
- Chinese cn = new Chinese();
- Japanese jp = new Japanese();
- Car car = new Car();
- House house = new House();
- DengJi(house);
- Console.ReadKey();
- }
- static void DengJi(IShow c)
- {
- Console.WriteLine("开始登记。。。");
- c.Show();
- }
- }
- public class Person
- {
- public string Name
- {
- get;
- set;
- }
- public virtual void Show()
- {
- }
- }
- public class Chinese : Person, IShow
- {
- public override void Show()
- {
- Console.WriteLine("我的户口是:xxxx");
- }
- #region IShow 成员
- void IShow.Show()
- {
- Console.WriteLine("中国人登记,我的户口是:xxxx");
- }
- #endregion
- }
- public class Japanese : Person, IShow
- {
- public override void Show()
- {
- Console.WriteLine("我家住在.日本......");
- }
- #region IShow 成员
- void IShow.Show()
- {
- Console.WriteLine("日本人登记,我家住在.日本......");
- }
- #endregion
- }
- public class Car : IShow
- {
- #region IShow 成员
- public void Show()
- {
- Console.WriteLine("车牌号码:京A88888");
- }
- #endregion
- }
- public class House : IShow
- {
- #region IShow 成员
- public void Show()
- {
- Console.WriteLine("房子登记!!!");
- }
- #endregion
- }
- public interface IShow
- {
- void Show();
- }
总结:
接口→抽象类→父类→具体类(在定义方法参数、返回值、声明变量的时候能用抽象就不要用具体。)
能使用接口就不用抽象类,能使用抽象类就不用类,能用父类就不用子类。
避免定义“体积庞大的接口”、“多功能接口”,会造成“接口污染”。只把相关联的一组成员定义到一个接口中(尽量在接口中少定义成员)。单一职责原则,定义多个职责单一的接口(小接口)(组合使用)。