C#通过完整的例子,Get常用的2个套路,理解抽象方法,虚方法,接口,事件
一.理解:抽象方法,虚方法,接口,事件
描述:
1.定义一个抽象父类“People”:
要求:
1>3个属性:名字,性别,年龄;
2>一个普通方法“说话”;
3>2个抽象方法:“喜欢什么”,“会做什么”
4>1个虚方法“母语”
5>定义一个接口,用于唱歌
2.分别创建3个子类:学生,老师,校长
要求:都继承抽象类People,而且都实现唱歌接口,老师覆写虚方法“母语”
3.每个子类都有自己独有的才艺,提供一个方法功能如下:
打印“开始表演才艺”
执行具体才艺
打印“才艺表演结束,大家鼓掌!!”
意义: 抽象类,虚方法
4.定义事件
要求:
抽象类中定义一个事件,模拟“发财了”。提供一个设置金钱的方法,默认1000作为触发条件,但老师需要1,0000触发,校长需要10,0000触发
意义: 理解观察者模式,理解事件的发布/触发,事件的观察者(订户),事件的订阅
5.新增一个“明星子类”
要求:
“明星”子类中,定义三个事件:表演开始/表演高潮/表演结束;
然后新增一个Show方法: 依次调用->说话->触发表演开始事件->喜欢什么->会做什么->触发表演高潮事件->才艺->触发表演结束事件
意义:理解事件的流程封装扩展的意义
一.搭建程序
自己新建一个工程,按照下图建好框架,拷贝下面的对应类即可
People类:中有2个常用的套路
常用套路1:
封装Skill的时候,用到了套路1
★★★适用情形:子类大部分动作都相同,个别不同的情况:
父类定义一个方法Skill(),大部分相同动作放Skill中;不同的动作在父类中做成一个抽象方法或者虚方法SelfSkill(),让各个子类去实现。
常用套路2:
模拟“发财了”。提供一个设置金钱的方法,做这个的时候又套路1,2,3中方式供选择
1>小套路1:Poeplez中定义一个 虚方法SetMoney,然后子类Teacher和Schoolmaster需要重载这个虚方法,然后子类虚方法里面设置不同的金额来触发GetRich事件
2>小套路2:把金钱在People中,定义为字段 protected int Money = 1000;,然后Teacher和Schoolmaster的构造函数中,重新对这个字段赋值即可
3>小套路3:在People中,定义一个委托判断条件,protected Func<int, bool> FuncJudge = i => i > 1000;,然后Teacher和Schoolmaster的构造函数中,重写委托即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Security.Policy; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Self.Interface 9 { 10 public abstract class People 11 { 12 //4.定义事件 13 //抽象类中定义一个事件,模拟“发财了”。提供一个设置金钱的方法,默认1000作为触发条件,但老师需要1,0000触发,校长需要10,0000触发 14 //套路:提供3中类型的套路 15 protected int Money = 1000;//套路2 把金钱做成字段,构造函数时候初始化 16 protected Func<int, bool> FuncJudge = i => i > 1000;//套路3 委托判断条件 17 18 public event Action GetRich;//发财了时间 19 public virtual void SetMoney(int monry) 20 { 21 //if (monry >= 1000)//套路1 虚方法重载 22 //if (monry >= Money)//套路2 把金钱做成字段,构造函数时候初始化 23 if (FuncJudge.Invoke(monry))//套路3 委托判断条件 24 { 25 FireGetRich(); 26 } 27 } 28 /// <summary> 29 /// 触发发财了事件 30 /// </summary> 31 protected void FireGetRich() 32 { 33 GetRich?.Invoke(); 34 } 35 public string Name { get; set; } 36 public string Age { get; set; } 37 public string Gender { get; set; } 38 public void Speak() 39 { 40 Console.WriteLine("我是抽象父类方法:现在我开始说话!"); 41 } 42 public abstract void Like(); 43 public abstract void ICanDo(); 44 public virtual void MotherTongue() 45 { 46 Console.WriteLine("我是抽象类:我的母语是汉语"); 47 } 48 /// <summary> 49 /// 3.每个子类都有自己独有的才艺,提供一个方法功能如下: 50 /// 打印“开始表演才艺” 51 /// 执行具体才艺 52 /// 打印“才艺表演结束,大家鼓掌!!” 53 /// 54 /// 常用的套路1: 55 /// ★★★适用情形:子类大部分动作都相同,个别不同的情况: 56 /// 父类定义一个方法Skill(),大部分相同动作放Skill中;不同的动作在父类中做成一个抽象方法或者虚方法SelfSkill(),让各个子类去实现。 57 /// </summary> 58 public void Skill() 59 { 60 Console.WriteLine("开始表演才艺"); 61 this.SelfSkill(); 62 Console.WriteLine("才艺表演结束,大家鼓掌!!"); 63 } 64 protected abstract void SelfSkill();//这里注意 受保护的protected 和每个子类都必须实现的abstract 65 } 66 }
Sing类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Self.Interface 8 { 9 public interface ISing 10 { 11 void Sing(); 12 } 13 }
4个子类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Self.Interface; 7 8 namespace Self.Service 9 { 10 public class Schoolmaster : People, ISing 11 { 12 public Schoolmaster() 13 { 14 base.Money = 100000;//2 把金钱做成字段,构造函数时候初始化 15 FuncJudge = i => i >= 100000;//套路3 委托判断条件 16 } 17 public override void Like() 18 { 19 Console.WriteLine("我喜欢看广场舞!"); 20 } 21 public override void ICanDo() 22 { 23 Console.WriteLine("我会管理学校"); 24 } 25 public void Sing() 26 { 27 Console.WriteLine("开始唱:我的中国心!"); 28 } 29 protected override void SelfSkill() 30 { 31 Console.WriteLine("我会拉小提琴"); 32 } 33 } 34 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Self.Interface; 7 8 namespace Self.Service 9 { 10 public class Teacher : People, ISing 11 { 12 public Teacher() 13 { 14 base.Money = 10000;//2 把金钱做成字段,构造函数时候初始化 15 FuncJudge = (i) => i >= 10000;//套路3 委托判断条件 16 } 17 public override void Like() 18 { 19 Console.WriteLine("我喜欢科研"); 20 } 21 public override void ICanDo() 22 { 23 Console.WriteLine("我可以教学"); 24 } 25 public void Sing() 26 { 27 Console.WriteLine("开始唱:新贵妃醉酒!"); 28 } 29 public override void MotherTongue() 30 { 31 Console.WriteLine("我是老师,我的母语是英语!"); 32 } 33 protected override void SelfSkill() 34 { 35 Console.WriteLine("我会跳拉丁舞"); 36 } 37 } 38 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Self.Interface; 7 8 namespace Self.Service 9 { 10 public class Student : People, ISing 11 { 12 public override void ICanDo() 13 { 14 Console.WriteLine("我会写作业!"); 15 } 16 17 public override void Like() 18 { 19 Console.WriteLine("我喜欢打游戏"); 20 } 21 22 public void Sing() 23 { 24 Console.WriteLine("开始唱:我的滑板鞋!"); 25 } 26 protected override void SelfSkill() 27 { 28 Console.WriteLine("我会打王者荣耀!"); 29 } 30 } 31 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Self.Interface; 7 8 9 namespace Self.Service 10 { 11 public class Star : People, ISing 12 { 13 public event Action StartOn; 14 public event Action HightOn; 15 public event Action EndOn; 16 /// <summary> 17 /// ★★★目的:理解事件的流程封装扩展的意义 18 ////封装框架的时候,固定的业务都封装好,开放出扩展点封装成事件;让用户自己定义逻辑。类似于事件的按钮情况 19 ///也可以理解为:程序设计的时候,固定部分写死,不固定部分通过一个事件封装出去。 20 /// </summary> 21 public void Show() 22 { 23 base.Speak();//固定部分写死 24 25 this.StartOn?.Invoke();//不固定部分,通过事件逻辑 26 27 this.Like(); 28 this.ICanDo(); 29 30 this.HightOn?.Invoke(); 31 32 this.Sing(); 33 34 this.EndOn?.Invoke(); 35 } 36 public override void ICanDo() 37 { 38 Console.WriteLine("我会演戏"); 39 } 40 41 public override void Like() 42 { 43 Console.WriteLine("我喜欢表演"); 44 } 45 46 public void Sing() 47 { 48 Console.WriteLine("开始唱:我爱我的祖国"); 49 } 50 51 protected override void SelfSkill() 52 { 53 Console.WriteLine("我一秒钟能哭出来"); 54 } 55 } 56 }
主程序类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Self.Service; 7 using Self.Interface; 8 9 namespace 自己模仿写的例子 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 Console.WriteLine("********************teacher************************"); 16 People teacher = new Teacher() 17 { 18 Name = "苍井空", 19 Age = "33", 20 Gender = "女", 21 }; 22 teacher.GetRich += () => { Console.WriteLine("我发财了!!"); };//当设置金钱>=10000的时候,执行这个lambda表达式 23 teacher.Like(); 24 teacher.ICanDo(); 25 teacher.MotherTongue(); 26 teacher.Skill(); 27 teacher.SetMoney(10); 28 teacher.SetMoney(10000); 29 Console.WriteLine("**********************Student**********************"); 30 Student student = new Student() 31 { 32 Name = "无名小辈", 33 Age = "22", 34 Gender = "男" 35 }; 36 student.Like(); 37 student.ICanDo(); 38 student.MotherTongue(); 39 student.Skill(); 40 Console.WriteLine("**********************star**********************"); 41 Star star = new Star() 42 { 43 Name = "颖宝宝", 44 Age = "28", 45 Gender = "女", 46 }; 47 star.StartOn += () => { Console.WriteLine("主持人讲话了,大家安静了,明星即将开始表演了"); }; 48 star.HightOn += () => { Console.WriteLine("主持人讲话了,明星表演的精彩吧!"); }; 49 star.EndOn += () => { Console.WriteLine("主持人讲话了,明星表演结束了,大家鼓掌!!"); }; 50 star.Show(); 51 Console.ReadKey(); 52 } 53 } 54 }
二.抽象方法和虚方法的区别
下图中,调用虚方法MotherTongue()的时候,学生类没有重写这个方法,就调用父类的MotherTongue();老师类重写了虚方法MotherTongue,调用的时候就调用重写的方法。
目的:
1>父类的虚方法,子类想重写就重写,不行重写就算了。调用的时候,子类有重写就调用子类重写的虚方法,没有重写就调用父类虚方法。
2>父类的抽象方法,子类必须都得重写,不管子类愿不愿意,都必须重写的。
三.理解程序封装的思想
实例化一个明星类,然后调用Show(),固定部分写死了,不固定的部分通过事件封装出去了,让用户去实现业务逻辑。
从图中可以看到,当Show()方法遇到事件的时候,就去执行用户定义的事件逻辑了,执行完用户定义的逻辑,再继续往下继续执行。
目的:
1>理解程序设计的时候,固定的部分写死,不固定部分,通过事件封装出去。