在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1.1什么是虚方法? 在父类中使用 virtual 关键字修饰的方法, 就是虚方法。在子类中可以使用 override 关键字对该虚方法进行重写。 Virtual方法也可以单独执行。 1.2虚方法语法 父类: public virtual 返回值类型 方法名() { 方法体代码; } 子类: public override 返回值类型 方法名() { 方法体代码; } class CatType { public virtual void Cry() { Console.WriteLine("深呼吸,张开嘴巴,开始:"); } } class Cat:CatType { public override void Cry() { base.Cry(); Console.WriteLine("喵喵喵"); } } class Tiger:CatType { public override void Cry() { base.Cry(); Console.WriteLine("咆哮"); } } 1.3.虚方法使用细节 ①将父类的方法标记为虚方法, 就是在父类方法的返回值前加 virtual 关键字,表示这个方法可以被子类重写。 ②子类重写父类方法, 在子类的方法的返回值前加 override 关键字。 ③父类中的虚方法, 子类可以重写, 也可以不重写。 ④父类中用 virtual 修饰的方法, 可以用于实现该方法共有的功能(比如初始化该方法), 然后在子类重写该方法时, 使用 base 关键字调用父类中的该方法。 2、多态之里氏转换原则2.1 面向对象六大原则在使用面向对象思想进行程序设计开发过程中, 有六大原则需要注意, 六大原则在面向对象编程中的地位类似于“ 马列主义” “ 毛XX思想” “ *理论” 等,作为编程的“ 指导思想” 和“ 行动指南” 存在的。 六大原则如下: ①单一职责原则; ②开闭原则; ③里氏转换原则; ④依赖倒置原则; ⑤接口隔离原则; ⑥迪米特原则; 这六大面向对象编程原则, 在后续中我们会一一介绍到, 本节课讲解第一个原则: 里氏转换原则!先定义一个子类,重写cry方法,增加monkey方法 class Cat:CatType { public override void Cry() { base.Cry(); Console.WriteLine("喵喵喵"); } public void Monkey() { Console.WriteLine("我是子类--Cat类"); } } 2.2 何为里氏转换①子类对象可以直接赋值给父类变量;
而且父类的变量cry方法也被重写了(override)。 ②子类对象可以调用父类中的成员, 但是父类对象永远只能调用自己的成员; CatType无法调用monkey(); ③如果父类对象中装的是子类对象, 可以将这个父类对象强转为子类对象; //现在方式. CatType ct = new Cat(); ct.Cry(); ct.MKCODE(); 目前ct虽然是子类对象,但是装在父类中,所以无法调用子类的monkey方法,强制转化之后,就可以使用monkey方法了 Cat c2 = (Cat)ct; c2.Monkey(); 这里我们用的是强制类型转换,也可以使用is 和 as 转换 is 和 as 两个关键字都可以进行类型转换。 is: 如果转换成功, 返回 true, 失败返回 false; as: 如果转换成功, 返回对应的对象, 失败返回 null。 bool mk = ct is Tiger; Console.WriteLine(mk); if(ct as Cat == null) { Console.WriteLine("转换失败"); }else{ Console.WriteLine("转换成功"); } 2.3 多态之抽象类语法2.3.1 抽象方法 虚方法到抽象方法 父类里面用 virtual 关键字修饰的方法叫做虚方法,子类可以使用override重新该虚方法,也可以不重写。 虚方法还是有方法体的,当我们父类中的这个方法已经虚到完全无法确定方法体的时候,就可以使用另外一种形式来表现,这种形式叫抽象方法。 2.3.2抽象方法语法 抽象方法的返回值类型前用关键字abstract修饰,且无方法体。 public abstract void Hello(); 抽象方法必须存在于抽象类中。 abstract class FuLei 在定义类的关键字class前面加abstract 修饰的类就是抽象类。 子类继承抽象类,使用 override关键字重写父类中所有的抽象方法。 2.3.3 抽象类注意事项 <1>抽象类中不一定要有抽象方法, 但是抽象方法必须存在于抽象类中。 <2>抽象类不能被实例化, 因为抽象类中有抽象方法(无方法体), 如果真能实例化抽象类的话, 调用这些无方法体的方法是没有任何意义的, 所以无法实例化。 2.3.4 使用场景 <1>当父类中的方法不知道如何去实现的时候, 可以考虑将父类写成抽象类,将方法写成抽象方法。 <2>如果父类中的方法有默认实现, 并且父类需要被实例化, 这时可以考虑将父类定义成一个普通类, 用虚方法实现多态。 <3>如果父类中的方法没有默认实现, 父类也不需要被实例化, 则可以将该类定义为抽象类。 2.3.5 抽象类编程案例前置回顾 <1>关于多态的实现方式已经介绍了虚方法,抽象类两种方式了。 <2>多态的使用前提,是建立在继承的关系之上的,也就是说必须要先有继承关系,然后才会出现多态。 <3>面向对象的封装,继承,多态,都是我们后期规划代码结构的基本思想。 <4>大点的项目可能会有几百个独立的脚本文件,这么多的脚本文件,如果没有一个代码结构框架来管理的话,项目十有八九是会中途夭折的。 案例:使用抽象类结构实现NPC模块 在游戏中会出现很多种不同用途的NPC,这些NPC有各自的存在的价值和作用,同时又具备一些共性的东西。在开发NPC系统的时候,往往是需要提取共性,独立出一个父类,然后子类继承实现不同作用的NPC。 分析: 任务 NPC, 商贩 NPC, 铁匠 NPC, 三种 NPC 的种类。 共有属性: npc 的名字, npc 的类型; 共有方法: 都能和玩家交互(交谈);
abstract class NPC { private string name; private NPCType type; public string Name { get { return name; } set { name = value; } } public NPCType Type { get { return type; } set { type = value; } } public NPC(string name, NPCType type) { this.name = name; this.type = type; } public abstract void Speak(); } class TaskNPC:NPC { private string taskInfo; public TaskNPC(string taskInfo, string name, NPCType type) : base(name, type) {//使用base,将name和type传递给父类,进行构造 this.taskInfo = taskInfo; } public override void Speak() { Console.WriteLine("NPC{0},任务{1}", base.Name, taskInfo); } } 2.3.6虚方法抽象类语法对比
2.4 多态之接口语法2.4.1 接口语法抽象类到接口 当抽象类中所有的方法都是抽象方法的时候,这个时候可以把这个抽象类用另外 一种形式来表现,这种形式叫接口。 虚方法,抽象类,接口是三种实现多态的手段。 语法格式要求: 接口使用 interface 关键字定义,没有 class 关键字,接口名一般使用 “IXxxx”
(实际使用要在interface前加public ,因为我有时候为了依赖注入,直接使用接口来装载子类对象)
这种方式进行书写, 在一堆脚本中通过名字判断, I 开头的都是接口。 接口中不能包含字段,但是可以包含属性(? 没有字段,如何写属性那? ?使用自动属性 public int Age {get;set;}) (公共字段只是类用public修饰符所公开的简单公共变量,而属性则是对字段的封装,它使用get和set访问器来控制如何设置或返回字段值。) 接口中定义的方法不能有方法体,全是抽象方法,但又不需要用 abstract 修饰; 接口中的成员不允许添加访问修饰符,默认都是 public;
(既然是接口里面的方法,当然需要从外面调用,必然是public了。) namespace xxx { interface IFly //实际的使用情况是,interface前面也有可能加public,里面的方法倒是不用加public。比如用接口的实例装载子类型对象 { //接口中不能包含字段. //private string name; //接口中的方法不能有方法体,不能有访问修饰符(默认是public) void Fly(); } } 2.4.2 接口注意事项<1>接口中所有的方法都是抽象方法,所以接口不能被实例化; <2>一个类可以实现多个接口,被实现的多个接口之间用逗号分隔开; class Batmobile:Car,IFly <3>一个接口可以继承多个接口, 接口之间也要用逗号分隔。 类与类之间只能单继承。 使用场景: 接口是一种能力,是一种规范,当我们对现在已经存在的类的继承关系进行功能扩展的时候,就可以使用接口来完成相应的工作。 具有特殊功能属性或者方法的子类,使用接口完成他的特殊点。 接口独立于原有的继承关系之外 2.5 多态之接口案例2.5.1 C#属性常规属性:先定义一个私有的字段,然后在为这个私有字段封装一个公开的属性,在属性中实现get和set两个方法,这种方式叫做常规属性。 当我们使用常规属性的时候,可以在get和set方法中,编写逻辑代码对取值和赋值进行逻辑的校验。这种方式是我们之前一直在使用的方式。 自动属性:在某些情况下,属性的get和set只是完成字段的取值和赋值操作,而不包含任何附加的逻辑代码,这个时候可以使用自动属性。 例如: public int Age {get;set;} 不用写字段,直接写属性 当我们使用自动属性的时候, 就不需要再写对应的字段了, C#编译器会自动给我们的自动属性提供一个对应的字段。 注意:在接口中使用属性,就要写自动属性的格式,因为接口中不支持字段。 2.5.2 接口案例模拟电脑USB接口 所有的电脑上都有 USB 接口,这些USB接口存在的目的是为了方便对电脑进行功能上的扩展,可以在这些接口上插U盘,移动硬盘,手机,外置光驱等等。之所以可以在USB接口上插入这些外置设备,是因为这些设备的接口都符合USB 接口的协议,符合了这个协议,才能使设备可以正常的和电脑连接。 编码实现: USB是一个接口。 U盘,移动硬盘,手机是具体的产品,这些产品在满足了自身功能的前提后,还需要实现这个USB接口规定的功能。 interface IUSB { void Read(); void Write(); } 2.6 多态之虚方法抽象类接口对比2.6.1 语法格式对比综合对比虚方法, 抽象类, 接口 三者的语法格式, 以及相关的关键字。
2.6.2使用场景对比虚方法:父类中的个别方法用虚方法实现,然后允许子类在有需要的情况下重写这些虚方法。 virtual和override 父类中包含虚方法也可以实例化对象。 抽象类:父类定义一系列的规范,子类去把父类里面定义的这些规范全部实现。 Abstract和override 父类是抽象类,那么不能单独实例化。 接口:是一种功能的扩展,是在原有的类的继承关系以外的新功能的扩展。 Interface Ixxxx void B1(); class Zi:Fu,IBBB { public void B1() { Console.WriteLine("B1"); } } 2.7多态之里氏转换原则案例2.7.1多态综合案例 模拟电脑与外部移动设备的关系: 创建三个类:电脑类,U盘类,移动硬盘类。 模拟外部存储设备插入电脑后,电脑对二者的存取操作。
1 class Computer 2 { 3 private string brand; 4 public IUSB USB_1; 5 public IUSB USB_2; 6 public string Brand 7 { 8 get { return brand; } 9 set { brand = value; } 10 } 11 public Computer(string brand) 12 { 13 this.brand = brand; 14 } 15 public void Start() 16 { 17 Console.WriteLine("{0}品牌的电脑开机中...", brand); 18 } 19 public void End() 20 { 21 Console.WriteLine("{0}品牌的电脑关机中...", brand); 22 } 23 interface IUSB 24 { 25 /// <summary> 26 /// 读取移动设备中的数据. 27 /// </summary> 28 void Read(); 29 /// <summary> 30 /// 往移动设备中写入数据. 31 /// </summary> 32 void Write(string content); 33 } 34 class HardDisk:Disk,IUSB 35 { 36 /// <summary> 37 /// 硬盘的存储空间. 38 /// </summary> 39 private string content; 40 public HardDisk(string brand) 41 : base(brand) 42 { 43 } 44 public void Read() 45 { 46 Console.WriteLine("{0}读取数据{1}", Brand, content); 47 } 48 public void Write(string content) 49 { 50 this.content += content; 51 Console.WriteLine("{0}存入数据{1}", Brand, content); 52 } 53 } 54 static void Main(string[] args) 55 { 56 UDisk u1 = new UDisk("金士顿32GB"); 57 HardDisk h1 = new HardDisk("三星500GB"); 58 Computer c1 = new Computer("联想"); 59 c1.Start(); 60 c1.USB_1 = u1; 61 c1.USB_1.Write("擅码网"); 62 c1.USB_1.Write("MKCODE"); 63 c1.USB_1.Read(); 64 c1.USB_2 = h1; 65 c1.USB_2.Write("mkcode.net"); 66 c1.USB_2.Write("lkk"); 67 c1.USB_2.Read(); 68 c1.End(); 69 Console.WriteLine(); 70 Computer c2 = new Computer("戴尔"); 71 c2.Start(); 72 c2.End(); 73 Console.ReadKey(); 74 } 这种算是面向接口开发。预留接口,进行后续扩展。
2.7.2多态概念回顾 在继承关系的前提下, 实例化出不同的对象, 这些对象调用相同的方法, 但是却 表现出不同的行为, 这就叫做多态。 参照https://www.cnblogs.com/qixinbo/ |
请发表评论