c#之事件详解
1.初步了解:
(1)事件:指的是能够发生的什么事情。比如公司上市,这里的上市就是事件(比事情更正式。)。
(2)在c#中的定义:是类型的成员。是一种使对象或类具备了通知能力的成员。
(3)事件参数:经由事件发送过来的,与事件本身相关的消息,称为事件参数。
作用:比如,当手机的关注者收到通知之后,就会检查事件参数(这里的包含这个通知中具体是开会还是别的信息),根据事件参数中的内容采取相应的行动。
(4)微事件处理器:软对这种对响应做出的相应处理称为响应事件或者处理事件,处理事件中具体所做的事情就叫做事件处理器。
(5)事件=使对象或类具备通知的能力+可选的事件参数(就是指的传来的详细信息)
(6)使用环境:用于对象或类之间的动作协调与信息传递(消息推送)。
某某对象有什么事件,是指这个对象可以通过这个事件来通知别的对象,事件一发生,关心这个事件的对象就会做出响应进行相应的处理。
(7)事件模型(它是从现实生活中抽象出来的一个客观存在)
发生->响应中的5个部分:闹钟响了你起床,孩子饿了你做饭。这2个例子都只有4部分,闹钟,响了,你,起床;孩子,饿了,你,做饭。还缺最后一部分,就是订阅关系。就是说你关注着某个事件。比如说为什么别人的闹钟响你不起床呢,因为你指订阅了自己的闹钟的事件,并且对事件做出了响应。
发生->响应中的5个动作(步骤):(1)我有一个事件。(2)一个人或一群人关心这个事件(3)我的事件发生了(4)关心这个事件的人依次接到通知(5)被通知到的人根据拿到的事件信息(又称事件数据,事件参数,通知)对事件进行响应(又称事件处理器)
(8)事件多用于桌面手机等的客户端编程。
2.事件的应用
事件不会主动发生,一定是事件的拥有者的内部逻辑触发的。
(1)事件模型的5个组成部分
·事件的拥有者(event source,对象)
·事件成员(event,成员)
·事件的响应者(event subscriber,对象)
·事件处理器(event handler,成员)--本质是一个回调方法
·事件订阅 把事件处理器和事件关联起来,本质上是一种以委托类型为基础的约定。用于订阅事件的事件处理器必须和事件遵守同一个约定。这个约定既约定了事件能够把什么样的消息发送给事件处理器,也约束了事件处理器能够处理什么样的消息。如果想订阅的事件和事件处理器遵守的约定是一样的,那就能够匹配。这个约定就是委托。所以常常说事件是基于委托的。之所以必须基于委托是因为这个事件处理器只能干这个事件所能允许干的事情,如果没有这个委托约束的话,比如说孩子饿了,事件处理器去打游戏,再比如说孩子困了,事件处理器去唱歌,这完全不符合常理,所以必须要有委托约束限制谁能来订阅我这个事件,事件处理器能够干什么。
class Program { static void Main(string[] args) { //timer事件拥有者 Timer timer = new Timer(); //boy是事件响应者 Boy boy = new Boy(); timer.Interval = 1000; //+=表示事件订阅,其中Elapsed是事件 ,其实我们可以先写Action方法名,这时候编译器会报错,
//然后根据报错提示自动在Boy类中生成Action这个事件的处理器方法就可以,因为一般初学者不知道Elaped等其它事件的约定是什么。可以通过编译器来实现 timer.Elapsed += boy.Action; timer.Start(); Console.ReadKey(); } } public class Boy { /// <summary> /// 事件处理器 /// </summary> /// <param name="sender">这是事件的拥有者,事件的source,或者事件的发送者(这三个说法都一样)</param> /// <param name="e">事件参数</param> internal void Action(object sender, ElapsedEventArgs e) { Console.WriteLine("elapsedEventArgs"); } }
事件的拥有者是timer,事件的响应者是类Boy,事件处理器Action是自己定义的一个方法,定时执行的方法。响应者订阅了之后就会定时执行这个事件处理器方法
(2)下面是事件的拥有者和响应者是不同的对象。
下面的代码是上图中的格式,事件的响应者和拥有者是不同的对象。
class Program { static void Main(string[] args) { //form是事件拥有者 Form form = new Form(); //controller是事件响应者 Controller controller = new Controller(form); Console.ReadKey(); } } public class Controller { private Form form; public Controller(Form form) { if(form!=null) { this.form = form; //事件是Click。 //事件处理器订阅事件FormClicked。 this.form.Click += this.FormClicked; } } /// <summary> /// 事件处理器 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FormClicked(object sender, EventArgs e) { this.form.Text = "test"; } }
(3)事件的拥有者和响应者是同一对象。
代码如下:
class Program { static void Main(string[] args) { //form是事件拥有者 MyForm form = new MyForm(); form.Click += form.FormClicked; Console.ReadKey(); } } public class MyForm:Form { /// <summary> /// 事件处理器 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> internal void FormClicked(object sender, EventArgs e) { this.Text = "test"; } }
(4)事件的拥有者是事件的相应者的一个字段成员,事件的响应者用方法订阅者自己的成员的某个事件。
实现点击按钮,在文本框中显示文本信息
class Program { static void Main(string[] args) { //form是事件拥有者,也是事件的响应者 MyForm form = new MyForm(); form.ShowDialog(); Console.ReadKey(); } } public class MyForm:Form { private TextBox textBox; private Button button; public MyForm() { this.textBox = new TextBox(); this.button = new Button(); this.Controls.Add(textBox); this.Controls.Add(button); //Click是事件,ButtonCliked订阅了事件Click 注意,如果已经自己拖拽了控件,那就写自己定义非控件名称,比如:this.button1.Click this.button.Click += this.ButtonCliked; } /// <summary> /// 事件处理器 /// </summary> /// <param name="sender">这是事件的拥有者,事件的source,或者事件的发送者</param> /// <param name="e">事件参数</param> private void ButtonCliked(object sender, EventArgs e) { if(sender==this.button) { this.textBox.Text = "test"; } } }
结果:
示例2(引用于:https://www.cnblogs.com/wayfarer/archive/2004/04/20/6712.html):
创建一个简单的类,名为FileWatch,包含事件OnfileChange。该类将检查在执行应用程序的目录(当前
目录,通常是项目名/bin/debug)下,是否存在文件test.txt。如果文件被删除或创建,都将触发事件。
同时提供一个方法MonitorFile以不断地查询该文件。
建立事件的要点:
(1)首先要创建一个委托FileWatchEventHandler,因为事件是基于委托的,是通过委托来进行约束事件响应器和事件的。
(2)然后创建一个事件字段FileWatchEvent,返回类型是委托类型。
(3)然后定义一个方法OnFileChangeEvent处理事件。
(4)总体来说就是Monitor()方法引起事件的产生,通过委托的约束,订阅了这个事件的响应者进行事件的处理,也就是执行OnFileChange()方法。
namespace MyEvent { /** * 首先线程启动(thd.Start()),然后调用MonitorFile()方法。引起事件产生,FileWatchEvent产生后,
由于我们将事件FileWatchEvent绑定到了OnFileChange()方法上。因而调用本地即窗口类的OnFileChange()方法,从而实现在ListBox中添加信息。 * **/ public partial class Form1 : Form { private FileWatch FileWatchEventSource; public Form1() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; FileWatchEventSource = new FileWatch();
FileWatchEventSource.FileWatchEvent += new FileWatchEventHandler(OnFileChange); var thrd = new Thread(new ThreadStart(FileWatchEventSource.MonitorFile)); thrd.Start(); } private void Form1_Load(object sender, EventArgs e) { }
//绑定事件的方法 private void OnFileChange(object Sender, EventArgs e) { listBox1.Items.Add(DateTime.Now.ToString() + ": 文件发生改变."); } } public delegate void FileWatchEventHandler(object sender, EventArgs e); public class FileWatch { private bool _bLastStatus = false; public FileWatch() { // // TODO: 在此处添加构造函数逻辑 // } public event FileWatchEventHandler FileWatchEvent; //调用事件的方法,执行这个方法之后就会执行绑定到当前事件字段上的那个方法 protected virtual void OnFileChangeEvent(EventArgs e) { FileWatchEvent?.Invoke(this, e); } public void MonitorFile() { bool bCurrentStatus; while (true) { bCurrentStatus = File.Exists(@"D:/test.txt"); //状态不符,说明文件被删除或重新创建,此时触发事件; if (bCurrentStatus != _bLastStatus) { _bLastStatus = bCurrentStatus; OnFileChangeEvent(EventArgs.Empty); } Thread.Sleep(1000); } } } }
结果:
3.事件的声明
事件是基于委托的。
第一层意思:就是说事件需要委托来做约束,这个约束既规定了事件能够发送什么样的消息给事件的响应者,也规定了事件的响应者返回什么样的消息,所以说这个事件的事件响应器必须和这个事件匹配上才能订阅这个事件。
第二层意思:当事件的响应者给事件的拥有者提供了能够匹配的事件处理器之后,需要找个地方将事件处理器记录保存下来,能够记录和引用方法的任务也只有委托能够实现。
总的来说,事件不管是从表层约束来讲,还是底层实现来讲,都是依赖于委托的。
(1)完整声明
比如去餐馆吃饭,这时候会有服务员来订阅你点菜的事件,当点菜的事件发生,服务员之后就会上菜。实际上服务员拿自己的事件处理器来处理你点菜的事件。现在我们声明实现一个点菜的事件。
namespace TestClass { class Program { static void Main(string[] args) { //customer是事件拥有者, Customer customer = new Customer(); //waitor是事件的响应者 Waitor waitor = new Waitor(); //Order是事件,Action是事件处理器 //订阅事件 customer.Order += waitor.Action; //订阅完事件,下面需要执行这个事件 customer.Action(); customer.PayTheBill(); Console.ReadKey(); } } /// <summary> /// 如果一个委托是为了声明一个事件准备的,那么需要后缀加上EventHandler /// 这个委托和类是平级的,因为委托本质上是类。 /// </summary> /// <param name="customer"></param> /// <param name="e"></param> public delegate void OrderEventHandler(Customer customer, OrderEventArgs e); /// <summary> /// 菜单信息类 /// 如果某个类是作为EventArgs事件参数来使用的话,那就需要继承EventArgs /// </summary> public class OrderEventArgs:EventArgs { /// <summary> /// 菜名 /// </summary> public string DishName; public string Size; } public class Customer { /// <summary> /// 账单 /// </summary> public double Bill; private OrderEventHandler orderEventHandler; /// <summary> /// 创建一个事件Order点菜 /// 这个事件是由OrderEventHandler委托进行约束的。 /// </summary> public event OrderEventHandler Order { add { this.orderEventHandler += value; } remove { this.orderEventHandler -= value; } } public void PayTheBill() { Console.WriteLine($"支付账单:{this.Bill}"); } public void WalkIn() { Console.WriteLine($"支付账单:{this.Bill}"); } public void SitDown() { Console.WriteLine($"支付账单:{this.Bill}"); } /// <summary> /// 思考吃什么 /// </summary> public void Think() { Console.WriteLine($"支付账单:{this.Bill}"); //先判断有没有订阅这个事件 if(this.orderEventHandler!=null) { OrderEventArgs orderEventArgs = new OrderEventArgs(); orderEventArgs.DishName = "Noodles"; orderEventArgs.Size = "Size"; this.orderEventHandler(this,orderEventArgs); } } public void Action() { Console.ReadLine(); this.WalkIn(); this.SitDown(); this.Think(); } } /// <summary> /// 服务生 /// </summary> public class Waitor { public void Action(Customer customer, OrderEventArgs e) { Console.Write($"DishName={e.DishName}"); double price = 10; switch(e.Size) { case "small": price *= 0.5; break; case "large": price *= 1.5; break; default: break; } customer.Bill += price; } } }
注意上面的代码,之所以使用EventHandler后缀,用意有3个:第一个是别人看到后就会知道这个委托是专门用来生成事件的。第二个是说明这个委托是用来约束事件处理器的。第三个是指这个委托将来是专门用来存储事件处理器的。
简化声明:
namespace TestClass { class Program { static void Main(string[] args) { //customer是事件拥有者, Customer customer = new Customer(); //waitor是事件的响应者 Waitor waitor = new Waitor(); //Order是事件,Action是事件处理器 //订阅事件 customer.Order += waitor.Action; //订阅完事件,下面需要执行这个事件 customer.Action(); customer.PayTheBill(); Console.ReadKey(); } } /// <summary> /// 如果一个委托是为了声明一个事件准备的,那么需要后缀加上EventHandler /// 这个委托和类是平级的,因为委托本质上是类。 /// </summary> /// <param name="customer"></param> /// <param name="e"></param> public delegate void OrderEventHandler(Customer customer, OrderEventArgs e); /// <summary> /// 菜单信息类 /// 如果某个类是作为EventArgs事件参数来使用的话,那就需要继承EventArgs /// </summary> public class OrderEventArgs:EventArgs { /// <summary> /// 菜名 /// </summary> public string DishName; public string Size; } public class Customer { /// <summary> /// 账单 /// </summary> public double Bill; /// <summary> /// 创建一个事件Order点菜 /// 这个事件是由OrderEventHandler委托进行约束的。 /// </summary> public event OrderEventHandler Order; public void PayTheBill() { Console.WriteLine($"支付账单:{this.Bill}"); } public void WalkIn() { Console.WriteLine($"支付账单:{this.Bill}"); } public void SitDown() { Console.WriteLine($"支付账单:{this.Bill}"); } /// <summary> /// 思考吃什么 /// </summary> public void Think() { Console.WriteLine($"支付账单:{this.Bill}"); //先判断有没有订阅这个事件 if(this.Order != null) { OrderEventArgs orderEventArgs = new OrderEventArgs(); orderEventArgs.DishName = "Noodles"; orderEventArgs.Size = "Size"; this.Order(this,orderEventArgs); } } public void Action() { Console.ReadLine(); this.WalkIn(); this.SitDown(); this.Think(); } } /// <summary> /// 服务生 /// </summary> public class Waitor { public void Action(Customer customer, OrderEventArgs e) { Console.Write($"DishName={e.DishName}"); double price = 10; switch(e.Size) { case "small": price *= 0.5; break; case "large": price *= 1.5; break; default: break; } customer.Bill += price; } } }
4.有了委托字段/属性,为什么还需要事件?
为了程序的逻辑更加的‘有道理’,更加安全,防止‘借刀杀人’
比如在餐馆中,明明你没有点别的菜,但是别人给点了,记到了自己上面。
如下:
namespace TestClass { class Program { static void Main(string[] args) { //customer是事件拥有者, Customer customer = new Customer(); //waitor是事件的响应者 Waitor waitor = new Waitor(); //Order是事件,Action是事件处理器 //订阅事件 customer.Order += waitor.Action; //订阅完事件,下面需要执行这个事件 //customer.Action(); #region 不用事件,使用委托,菜品信息都是外部传入,没法保证安全性,使用事件的话,都是在自己的内部类中决定的菜品,给外部只留是否订阅事件的选项 OrderEventArgs orderEventArgs = new OrderEventArgs() { DishName = "test1", Size="small" }; OrderEventArgs orderEventArgs1 = new OrderEventArgs() { DishName = "test2", Size = "large" }; Customer badCustomer = new Customer(); badCustomer.Order += waitor.Action; badCustomer.Order(customer, orderEventArgs1); badCustomer.Order(customer, orderEventArgs); #endregion customer.PayTheBill(); Console.ReadKey(); } } /// <summary> /// 如果一个委托是为了声明一个事件准备的,那么需要后缀加上EventHandler /// 这个委托和类是平级的,因为委托本质上是类。 /// </summary> /// <param name="customer"></param> /// <param name="e"></param> public delegate void OrderEventHandler(Customer customer, OrderEventArgs e); /// <summary> /// 菜单信息类 /// 如果某个类是作为EventArgs事件参数来使用的话,那就需要继承EventArgs /// </summary> public class OrderEventArgs : EventArgs { /// <summary> /// 菜名 /// </summary> public string DishName; public string Size; } public class Customer { /// <summary> /// 账单 /// </summary> public double Bill; /// <summary> /// 创建一个事件Order点菜 /// 这个事件是由OrderEventHandler委托进行约束的。 /// </summary> //public event OrderEventHandler Order; #region 不用事件,使用委托 //创建委托字段 public OrderEventHandler Order; #endregion public void PayTheBill() { Console.WriteLine($"支付账单:{this.Bill}"); } public void WalkIn() { Console.WriteLine($"支付账单:{this.Bill}"); } public void SitDown() { Console.WriteLine($"支付账单:{this.Bill}"); } /// <summary> /// 思考吃什么 /// </summary> public void Think() { Console.WriteLine($"支付账单:{this.Bill}"); //先判断有没有订阅这个事件 if (this.Order != null) { OrderEventArgs orderEventArgs = new OrderEventArgs(); orderEventArgs.DishName = "Noodles"; orderEventArgs.Size = "Size";
//因为事件是从事件拥有者的内部触发的, this.Order(this, orderEventArgs); } } public void Action() { Console.ReadLine(); this.WalkIn(); this.SitDown(); this.Think(); } } /// <summary> /// 服务生 /// </summary> public class Waitor { public void Action(Customer customer, OrderEventArgs e) { Console.Write($"DishName={e.DishName}"); double price = 10; switch (e.Size) { case "small": price *= 0.5; break; case "large": price *= 1.5; break; default: break; } customer.Bill += price; } } }
需要注意的是,在开发的时候,很多项目是很多人一起做,如果没有做限制的话,很可能会被滥用。如果是通过事件的话,就会确保只有订阅了这个事件的事件响应者才会和这个事件有联系。事件的触发只能是事件的拥有者才能触发,比委托更安全。
把上面代码中 public event OrderEventHandler Order;这行代码的event去掉,就会报错,这种使用有问题( badCustomer.Order(customer, orderEventArgs1);): 事件“Customer.Order”只能出现在 += 或 -= 的左边(从类型“Customer”中使用时除外) 。
5.事件的本质是委托字段的包装器
(1)这个包装器对委托字段的访问起限制作用
(2)封装的一个重要功能就是隐藏
(3)事件对外界隐藏了委托字段的大部分功能,仅暴露添加/移除事件处理器的功能。(所以上面代码将委托字段改成事件(加上event)之后就会报错,事件对于本类的外界来说,只暴露了添加/移除事件处理器的功能)
6.通用的事件约束
下面是一个通用的事件约束
代码:
namespace TestClass { class Program { static void Main(string[] args) { //customer是事件拥有者, Customer customer = new Customer(); //waitor是事件的响应者 Waitor waitor = new Waitor(); //Order是事件,Action是事件处理器 //订阅事件 customer.Order += waitor.Action; //订阅完事件,下面需要执行这个事件 customer.Action(); customer.PayTheBill(); Console.ReadKey(); } } /// <summary> /// 菜单信息类 /// 如果某个类是作为EventArgs事件参数来使用的话,那就需要继承EventArgs /// </summary> public class OrderEventArgs : EventArgs { /// <summary> /// 菜名 /// </summary> public string DishName; public string Size; } public class Customer { /// <summary> /// 账单 /// </summary> public double Bill; /// <summary> /// 创建一个事件Order点菜 /// 这个事件是由EventHandler委托进行约束的。 /// </summary> public event EventHandler Order; public void PayTheBill() { Console.WriteLine($"支付账单:{this.Bill}"); } public void WalkIn() { Console.WriteLine($"支付账单:{this.Bill}"); } public void SitDown() { Console.WriteLine($"支付账单:{this.Bill}"); } /// <summary> /// 思考吃什么 /// </summary> public void Think() { Console.WriteLine($"支付账单:{this.Bill}"); //先判断有没有订阅这个事件 if (this.Order != null) { OrderEventArgs orderEventArgs = new OrderEventArgs(); orderEventArgs.DishName = "Noodles"; orderEventArgs.Size = "Size"; this.Order(this, orderEventArgs); } } public void Action() { Console.ReadLine(); this.WalkIn(); this.SitDown(); this.Think(); } } /// <summary> /// 服务生 /// </summary> public class Waitor { public void Action(object obj, EventArgs args) { Customer customer = (Customer)obj; OrderEventArgs e = args as OrderEventArgs; Console.Write($"DishName={e.DishName}"); double price = 10; switch (e.Size) { case "small": price *= 0.5; break; case "large": price *= 1.5; break; default: break; } customer.Bill += price; } } }
7.触发事件Foo,一般谢伟OnFoo,而且级别一般是protected级别,防止“借刀杀人”
所以修改后的代码:
namespace TestClass { class Program { static void Main(string[] args) { //customer是事件拥有者, Customer customer = new Customer(); //waitor是事件的响应者 Waitor waitor = new Waitor(); //Order是事件,Action是事件处理器 //订阅事件 customer.Order += waitor.Action; //订阅完事件,下面需要执行这个事件 customer.Action(); customer.PayTheBill(); Console.ReadKey(); } } /// <summary> /// 菜单信息类 /// 如果某个类是作为EventArgs事件参数来使用的话,那就需要继承EventArgs /// </summary> public class OrderEventArgs : EventArgs { /// <summary> /// 菜名 /// </summary> public string DishName; public string Size; } public class Customer { /// <summary> /// 账单 /// </summary> public double Bill; /// <summary> /// 创建一个事件Order点菜 /// 这个事件是由EventHandler委托进行约束的。 /// </summary> public event EventHandler Order; public void PayTheBill() { Console.WriteLine($"支付账单:{this.Bill}"); } public void WalkIn() { Console.WriteLine($"支付账单:{this.Bill}"); } public void SitDown() { Console.WriteLine($"支付账单:{this.Bill}"); } /// <summary> /// 思考吃什么 /// </summary> public void Think() { Console.WriteLine("Thinking"); this.OnOrder("chicken","large"); } protected void OnOrder(string name,string size) { //先判断有没有订阅这个事件 if (this.Order != null) { OrderEventArgs orderEventArgs = new OrderEventArgs(); orderEventArgs.DishName = name; orderEventArgs.Size = size; this.Order(this, orderEventArgs); } } public void Action() { Console.ReadLine(); this.WalkIn(); this.SitDown(); this.Think(); } } /// <summary> /// 服务生 /// </summary> public class Waitor { public void Action(object obj, EventArgs args) { Customer customer = (Customer)obj; OrderEventArgs e = args as OrderEventArgs; Console.Write($"DishName={e.DishName}"); double price = 10; switch (e.Size) { case "small": price *= 0.5; break; case "large": price *= 1.5; break; default: break; } customer.Bill += price; } } }
8.事件的命名约定
(1)带有时态的动词或者动词短语
(2)正在做什么用进行时,完成做什么就用完成时。
委托是一个类,事件是委托的实例,比如委托是一个Student类,那么事件就是具体的学生张三,李四。
补充
为什么要使用事件?
一开始方法的逻辑很多,而且不稳定,想把不稳定的逻辑转移一下,可以通过事件。
案例1:比如猫进食,比如有统一的步骤:有洗爪子,围餐巾,吃东西,然后洗爪子,打嗝,剔牙。
public class Cat { /// <summary> /// 进食--不同的食物 /// </summary> /// <param name="httpContext">食物</param> public void Eat(string httpContext) { Console.WriteLine("洗爪子1"); Console.WriteLine("围餐巾"); Console.WriteLine($"eating{httpContext}"); Console.WriteLine("洗爪子2"); Console.WriteLine("打嗝"); Console.WriteLine("剔牙"); } }
调用:
class Program { static void Main(string[] args) { Cat cat = new Cat(); cat.Eat("猫粮"); Console.WriteLine("*************************"); cat.Eat("面包"); Console.WriteLine("*************************"); } }
但是不同的猫在这些步骤前后可能会做一些不同的事情,除了固定每只猫都有自己的其他习惯,我们不能来一只猫就要改Eat方法,所以我们可以通过事件来解决这个问题。
public class Cat { /// <summary> /// 进食--不同的食物 /// </summary> /// <param name="httpContext">食物</param> public void Eat(string httpContext) { if(CleanHand1Before!=null) { CleanHand1Before.Invoke(); } Console.WriteLine("洗爪子1"); if (CleanHand1After != null) { CleanHand1After.Invoke(); } if (NapkinHand1Before != null) { NapkinHand1Before.Invoke(); } Console.WriteLine("围餐巾"); if (NapkinHand1After != null) { NapkinHand1After.Invoke(); } Console.WriteLine($"eating{httpContext}"); if (CleanHand2Before != null) { CleanHand2Before.Invoke(); } Console.WriteLine("洗爪子2"); if (CleanHand2After != null) { CleanHand2After.Invoke(); } if (BurpHand1Before != null) { BurpHand1Before.Invoke(); } Console.WriteLine("打嗝"); if (BurpHand1After != null) { BurpHand1After.Invoke(); } if (PickHand1Before != null) { PickHand1Before.Invoke(); } Console.WriteLine("剔牙"); if (PickHand1After != null) { PickHand1After.Invoke(); } } #region 步骤之前 public event Action CleanHand1Before; //围餐巾 public event Action NapkinHand1Before; public event Action CleanHand2Before; //打嗝 public event Action BurpHand1Before; //剔牙 public event Action PickHand1Before; #endregion #region 步骤之后 public event Action CleanHand1After; //围餐巾 public event Action NapkinHand1After; public event Action CleanHand2After; //打嗝 public event Action BurpHand1After; //剔牙 public event Action PickHand1After; #endregion }
调用:
class Program { static void Main(string[] args) { Cat cat = new Cat(); cat.PickHand1Before += () => Console.WriteLine("cat剔牙之前洗脸"); cat.Eat("猫粮"); Console.WriteLine("*************************"); Cat cat1 = new Cat(); cat1.PickHand1Before += () => Console.WriteLine("cat1剔牙之前运动"); cat1.Eat("鱼儿"); Console.WriteLine("*************************"); } }
结果:
洗爪子1
围餐巾
eating猫粮
洗爪子2
打嗝
cat剔牙之前洗脸
剔牙
*************************
洗爪子1
围餐巾
eating鱼儿
洗爪子2
打嗝
cat1剔牙之前运动
剔牙
*************************
所以,可以根据实际业务情况来自由增加删除事件,Eat方法也不必每次都做改动。
引于:https://www.bilibili.com/video/BV13b411b7Ht?p=21
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术