面向对象---多态
(一).多态概念
让一个对象能够表现出多种状态(类型)
(二).实现多态三种方法
1.虚方法;2.抽象类;3.接口
(三).虚方法(关键字virtual)
当父类的函数有实现时使用虚方法。使用虚方法意味着这个父类函数可以被子类重写,具体调用哪个被子类重新的函数,取决于父类对象装的是哪一个子类对象,但调用的始终是父类函数。
作用:最大可能地取消各个子类对象中的差异性,减少代码冗余,增强程序的可读性。
例子:
员工类:
class Employee { public virtual void clockOut() { Console.WriteLine("员工九点打卡"); } } class Manager:Employee { public override void clockOut() { Console.WriteLine("经理十点打卡"); } } class Programmer:Employee { public override void clockOut() { Console.WriteLine("程序猿不打卡"); } }
实现:
class Program { static void Main(string[] args) { //员工九点打卡,经理十点打卡,程序猿不打卡 Employee emp = new Employee(); Manager mg = new Manager(); Programmer pg = new Programmer(); Employee[] emps = { emp, mg, pg }; for (int i = 0; i < emps.Length; i++) { emps[i].clockOut(); } Console.ReadKey(); } }
结果:
(四).抽象类(使用关键字abstract)
当父类的方法不知道如何实现的时候,可以将父类写成抽象类,将方法写成抽象方法。
注意:
1.抽象类不允许创建对象。
2.如果一个子类继承了一个抽象父类,那么它必须实现父类所有的抽象成员。
3.抽象成员必须标记为abstract,并且不能有任何实现。
4.抽象成员必须在抽象类中。抽象类可包含非抽象成员,并且抽象类中的实例成员可以不被子类发现。其中非抽象成员父类不可用,因为不能创建对象,但继承它的子类可以调用。
5.子类继承抽象类后,必须把父类所有的抽象成员重写。但如果这个子类也是抽象类,那就不用重写。
6.抽象成员的访问修饰符不能是private。
7.子类重写父类方法时返回的签名,必须跟抽象父类抽象方法的签名一样。签名指的是方法的返回值和参数。
例子:
动物类:
public abstract class Animals { //抽象类中可以有非抽象成员 private int _age; //年龄 public int Age { get { return _age; } set { _age = value; } } public Animals(int age) { this.Age = age; } public abstract void Bark(); //动物叫的方式 } public class Dog:Animals { public Dog(int age) : base(age) { } public override void Bark() //重写父类函数 { Console.WriteLine("小狗{0}岁了,会汪汪地叫~", this.Age); } } public class Cat : Animals { public Cat(int age) : base(age) { } public override void Bark() { Console.WriteLine("小猫{0}岁了,会喵喵地叫~", this.Age); } }
实现:
class Program { static void Main(string[] args) { //小狗汪汪叫,小猫喵喵叫 Animals dog = new Dog(3); Animals cat = new Cat(3); dog.Bark(); cat.Bark(); Console.ReadKey(); } }
结果:
虚方法和抽象类的区别:
如果父类中的方法有默认的视线,并且父类需要被实例化时,可以将父类定义成一个普通类,用虚方法实现多态。
如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。
(五).序列化和反序列化
序列化:将对象转化为二进制。
反序列化:将二进制转化为对象。
作用:以二进制的形式传输数据。
要将一个类标记为被序列化的,在类的上方加上[Serializable]。
例子:对一个文本的内容进行序列化和反序列化
人类:
namespace Serialization_And_Deserialization { [Serializable] public class Person { private string _name; //名字 public string Name { get { return _name; } set { _name = value; } } private char _gender; //性别 public char Gender { get { return _gender; } set { _gender = value; } } private int _age; //年龄 public int Age { get { return _age; } set { _age = value; } } } }
实现:
class Program { static void Main(string[] args) { Person p = new Person(); p.Name = "chaara"; p.Gender = '女'; p.Age = 12; //序列化,把文本文字(对象)转化为二进制存储在文本中 using (FileStream fsWrite = new FileStream(@"C:\Users\Desktop\new.txt", FileMode.OpenOrCreate, FileAccess.Write)) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fsWrite, p); } Console.WriteLine("序列化完毕!"); //反序列化,把存储在文本的二进制转换成文字(对象)打印出来 using (FileStream fsRead = new FileStream(@"C:\Users\Desktop\new.txt", FileMode.Open, FileAccess.Read)) { BinaryFormatter bf = new BinaryFormatter(); p = (Person)bf.Deserialize(fsRead); Console.WriteLine(p.Name); Console.WriteLine(p.Gender); Console.WriteLine(p.Age); Console.ReadKey(); } } }
结果:
序列化
反序列化
(六).接口(关键字interface)
需要多继承时使用接口。接口就是一个规范、能力。接口命名一般为I...able。
注意:
1.接口的成员可以有:方法、属性、索引器、事件(本质上都是方法)。
2.为了多态,接口不能被实例化。
3.接口中的成员默认修饰符为public,不能有任何的实现。说白了就是光说不做,定义一组未实现的成员。
4.接口与接口之间可以继承,并且可以多继承。
5.实现接口的子类必须要实现该接口的全部成员。
6.一个类如果同时继承了类和接口,类必须排在接口前面。
7.显示实现接口是为了解决方法的重名问题。
例子:
接口:
namespace Interface { public class Person : ISingingable { public void Singing() { Console.WriteLine("人在唱歌~"); } } public interface IFlyable //飞的能力 { void Fly(); //接口中的成员不允许添加访问修饰符,默认为public;不允许有具体方法体的函数和字段(不存储数据),但有自动属性 } public interface ISingingable //唱歌的能力 { void Singing(); } public interface IDanceable //唱歌的能力 { void Dance(); } public interface SuperIterface : ISingingable, IFlyable, IDanceable //超级接口,实现接口与接口之间的多继承 { } public class Bird : SuperIterface { public void Fly() { Console.WriteLine("小鸟在飞翔~"); } public void Dance() { Console.WriteLine("小鸟在跳舞~"); } public void Singing() { Console.WriteLine("小鸟在唱歌~"); } } }
实现:
class Program { static void Main(string[] args) { ISingingable s1 = new Person(); //创建子类对象赋值给接口 SuperIterface s = new Bird(); s1.Singing(); s.Fly(); s.Dance(); s.Singing(); Console.ReadKey(); } }
结果:
练习:真的鸭子 、橡皮鸭子和木头鸭子游泳方式
不同鸭子的接口
namespace 接口练习 { //真的鸭子会游泳 橡皮鸭子会游泳 木头鸭子不会游泳 public interface ISwingingable { void Swinging(); } public class RealDuck:ISwingingable { public void Swinging() { Console.WriteLine("真的鸭子靠翅膀脚丫子游泳"); } } public class RubberDuck : ISwingingable { public void Swinging() { Console.WriteLine("橡皮鸭子飘在水中自由泳"); } } public class WoodDuck : ISwingingable { public void Swinging() { Console.WriteLine("木头鸭子太重了游不动"); } } }
实现:
class Program { static void Main(string[] args) { ISwingingable realDuck = new RealDuck(); //创建真鸭子对象 ISwingingable rubberDuck = new RubberDuck();//创建橡皮鸭子对象 ISwingingable woodDuck = new WoodDuck(); //创建木头鸭子对象 realDuck.Swinging(); rubberDuck.Swinging(); woodDuck.Swinging(); Console.ReadKey(); } }
结果:
课后思考:
什么时候用虚方法?
什么时候用抽象类?
什么时候用接口?