初步了解事件

订阅关系:关注的对象,我关注着自己的闹钟,当自己闹钟响了,执行起床的方法,而非别人的闹钟。
事件的订阅者:
事件参数:
设计模式:
MVC、MVP、MVVM等模式,是事件模式更高级、更有效的“玩法”。
事件的应用
事件模型的五个组成部分
1.事件的拥有者/事件的Source(event source,对象/类)
事件不会主动发生,一定是被拥有者某些内部逻辑触发后才能够发生,才能够发挥通知的作用。
2.事件成员(event ,成员)
3.事件的响应者(event subscriber,对象) 被通知了,事件响应者是订阅了事件的对象或者类
4.事件处理器(event handler,方法成员)—本质上是一个回调方法
5.事件订阅—把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的“约定”
解决三个问题:谁被通知/拿什么事件处理器来处理事件/遵守同一个“约定”
Examples:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Timers;/// <summary>/// 事件拥有者(事件Source)是timer对象,事件响应者boy,该响应者需要一个事件处理器Action方法/// 事件订阅 timer.Elapsed+=boy.Action/// </summary>namespace EventModel{class Program{static void Main(string[] args){Timer timer = new Timer();timer.Interval = 1000;//时间间隔,每隔1000ms就会触发Elapsed事件Boy boy = new Boy();Girl girl = new Girl();timer.Elapsed += boy.Action;timer.Elapsed += girl.Action;//先订阅先执行timer.Start();Console.ReadLine();}}class Boy{internal void Action(object sender, ElapsedEventArgs e){Console.WriteLine("Jump");}}class Girl{internal void Action(object sender, ElapsedEventArgs e){Console.WriteLine("Dance");}}}
标准事件模型
事件的拥有者和事件的响应者是完全不同的两个对象
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace EventModelNormal{class Program{static void Main(string[] args){Form form = new Form(); //事件拥有者是formController controller = new Controller(form);//事件响应者是Controller实例form.ShowDialog();}}class Controller{private Form form;public Controller(Form form){if(form!=null){this.form = form;this.form.Click += this.FormClicked; //事件响应者是Controller实例,订阅关系}}private void FormClicked(object sender, EventArgs e){this.form.Text = DateTime.Now.ToString();}}}
其他事件模型
二星实例:事件的拥有者和事件的响应者是同一个对象
方法一:两次Tab,事件处理器和事件响应者为其本身
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace EventModelTwo{//派生:在原有类的基础上拓展功能class Program{static void Main(string[] args){Form form = new Form();//事件的拥有者form.Click += Form_Click;form.ShowDialog();}private static void Form_Click(object sender, EventArgs e){Form form = (Form)sender;form.Text = DateTime.Now.ToString();}}}
方法二:继承基类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace EventModelTwo{//派生(继承):在原有类的基础上拓展功能class Program{static void Main(string[] args){MyForm myForm = new MyForm();//事件的拥有者MyForm对象,Click事件,事件响应者MyForm//事件处理器 Form_Clicked 事件订阅+=myForm.Click += myForm.Form_Clicked;myForm.ShowDialog();}}class MyForm : Form{internal void Form_Clicked(object sender, EventArgs e){this.Text = DateTime.Now.ToString();}}}
三星例子:事件拥有者是事件响应者的一个字段成员,事件响应者用自己的一个方法订阅者自己字段成员的某个事件
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace EventModelImportant{class Program{static void Main(string[] args){MyForm myForm = new MyForm();//事件响应者myForm,事件拥有者是类成员Button,事件:ClickmyForm.ShowDialog();}}class MyForm :Form{private TextBox textBox;private Button button;private FlowLayoutPanel layoutPanel;public MyForm(){this.textBox = new TextBox();this.button = new Button();this.layoutPanel = new FlowLayoutPanel();this.Controls.Add(this.textBox);this.Controls.Add(this.button);this.button.Top = this.textBox.Height;this.button.Text = "Say Hello";this.button.Click += this.Button_Click;}private void Button_Click(object sender, EventArgs e){string text = "Hello AlbertZhaohongyong";this.textBox.Text = text;this.textBox.Width = text.Length*8;}}}
小知识点:
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace Event小知识点{public partial class Form1 : Form{public Form1(){InitializeComponent();//Lmd表达式//写法一:this.button1.Click += (object sender, EventArgs e) =>{this.textBox1.Text = "Hello AlbertZhaohongyong";};//写法二:在一的基础上简化,不需要写委托约束this.button1.Click += (sender,e) =>{this.textBox1.Text = "Hello AlbertZhaohongyong";};//写法三:已过时,匿名方法this.button1.Click += delegate (object sender, EventArgs e){this.textBox1.Text = "AlbertZhaohongyong";};}}}
类的三大成员:属性(类的状态)、方法(类能够做什么)、事件(通知谁)
深入理解事件
事件的声明
自定义事件的产生的原因
非产商为我们准备好直接可用的事件(例如Click,Elapse等)
事件是基于委托的:(表层约束,底层实现)
1.事件需要使用委托类型做一个约束,这个约束既规定了事件能够发送什么样消息给事件响应者,也规定了事件响应者能收到什么样的消息。(匹配)
2.当事件的响应者向事件的拥有者提供了事件处理器之后,能够记录和引用方法的任务只有委托类型的实例能够做到。
总结:委托是事件的底层基础,事件是委托的上层建筑。
事件声明的完整格式
//using EventFullDeclare;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;namespace EventFullDeclare{class Program{static void Main(string[] args){Customer customer = new Customer();Waiter waiter = new Waiter();customer.Order += waiter.Action;customer.Action();customer.PayTheBill();}}public class OrderEventArgs:EventArgs //派生自EventArgs基类{public string DishName { get; set; }public string Size { get; set; }}public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);//准备好了委托类型//基于事件的委托,以事件名+EventHandler为后缀(委托名),第一个参数为事件Source,第二个参数为事件名+EventArgs为后缀public class Customer{private OrderEventHandler orderEventHandler;public event OrderEventHandler Order{//事件处理器的添加器add{this.orderEventHandler += value;}//事件处理器的移除器remove{this.orderEventHandler -= value;}}public double Bill { get; set; }public void PayTheBill(){Console.WriteLine("I will pay ${0}", Bill);}public void WalkIn(){Console.WriteLine("Walk in the restaurant.");}public void SitDown(){Console.WriteLine("SitDown");}public void Think(){Thread.Sleep(1000);if(this.orderEventHandler!=null){OrderEventArgs e = new OrderEventArgs();e.DishName = "Kongpao Chicken";e.Size = "large";this.orderEventHandler.Invoke(this, e);}}public void Action(){Console.ReadLine();this.WalkIn();this.SitDown();this.Think();}}public class Waiter{public void Action(Customer customer, OrderEventArgs e){Console.WriteLine("I will serve you the dish - {0}",e.DishName);double price = 10;switch(e.Size){case "small":price = price * 0.5;break;case "large":price = price * 1.5;break;default:break;}customer.Bill += price;}}}
Winform中自定义事件封装声明
//子窗体using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace EventHandler{public partial class SubForm : Form{//自定义事件public event controlClickEventHandler controlClick;//事件拥有者内部逻辑触发事件方法protected virtual void TrigEvent(EventMessageEventArgs e){if(controlClick!=null){controlClick.Invoke(this, e);}}//引发事件private void RaiseEvent(string raiseEventMessage){EventMessageEventArgs e = new EventMessageEventArgs(raiseEventMessage);TrigEvent(e);}public SubForm(){InitializeComponent();}private void SubForm_Load(object sender, EventArgs e){}private void button1_Click(object sender, EventArgs e){this.textBox1.Text = "this is subform";RaiseEvent("ButtonClick");}}#region 自定义事件public delegate void controlClickEventHandler(object sender, EventMessageEventArgs e);public class EventMessageEventArgs : EventArgs{public string Message { get; set; }public EventMessageEventArgs(string messageInfo){Message = messageInfo;}}#endregion}//主窗体using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace EventHandler{public partial class Form1 : Form{public SubForm subForm = new SubForm();public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){subForm.TopLevel = false;this.panel2.Controls.Add(subForm);subForm.Show();subForm.controlClick += this.Action;}private void Action(object sender, EventMessageEventArgs e){if (e.Message == "ButtonClick"){this.textBox1.Text = "This is text form subform";}}}}
事件简化声明
field-like(像字段的声明格式)
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EventSimply{public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);class Program{static void Main(string[] args){Customer customer = new Customer();Waiter waiter = new Waiter();customer.order += waiter.Action;customer.Aciton();}}public class OrderEventArgs:EventArgs{public string DishName { get; set; }public string Size { get; set; }}public class Customer{public event OrderEventHandler order;//事件的本质是委托字段的一个包装器public double Bill { get; set; }public void PayBill(){Console.WriteLine("I will pay {0}",Bill);}public void Aciton() //事件永远是拥有者内部逻辑触发{if(order!=null){OrderEventArgs e = new OrderEventArgs();e.DishName = "Kongpao Chicken";e.Size = "large";order.Invoke(this, e);}}}public class Waiter{internal void Action(Customer customer, OrderEventArgs e){double prize = 10;switch(e.Size){case "large":prize = prize * 1.5;break;default:break;}customer.Bill = prize;Console.WriteLine("You need pay {0} for {1}",customer.Bill,e.DishName);}}}
调用x64 Native Tools Command Prompt for VS 2019
输入ildasm

水蓝色字段Order,事件的本质是委托字段的一个包装器
问题辨析
我们既然可以声明委托类型的字段,为什么还要事件这种成员呢?
更加有逻辑,更加有道理,防止“借刀杀人”,事件可以保证不能在事件拥有者的外部随随便便触发事件,只能在外部+=或者-=。
封装(encapsulation)的一个重要功能就是隐藏,事件**对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能。
触发事件方法一般命名为OnFoo,即“因何引发”、“事出有因”,访问级别为protected,不能为public 不然又成了可以“借刀杀人”了。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EventSimply{public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);class Program{static void Main(string[] args){Customer customer = new Customer();Waiter waiter = new Waiter();customer.order += waiter.Action;//customer.Aciton();Customer badGuy = new Customer();OrderEventArgs e = new OrderEventArgs();e.DishName = "Manhanquanxi";e.Size = "large";OrderEventArgs e2 = new OrderEventArgs();e2.DishName = "beer";e2.Size = "large";badGuy.order += waiter.Action;badGuy.order.Invoke(customer, e2);//借刀杀人,将e2给到第一个客人头上customer.Aciton();}}public class OrderEventArgs:EventArgs{public string DishName { get; set; }public string Size { get; set; }}public class Customer{public OrderEventHandler order;//直接使用委托,外部也能进行访问public double Bill { get; set; }public void PayBill(){Console.WriteLine("I will pay {0}",Bill);}public void Aciton() //事件永远是拥有者内部逻辑触发,触发事件方法一般命名为OnFoo{if(order!=null){OrderEventArgs e = new OrderEventArgs();e.DishName = "Kongpao Chicken";e.Size = "large";order.Invoke(this, e);}}}public class Waiter{internal void Action(Customer customer, OrderEventArgs e){double prize = 10;switch(e.Size){case "large":prize = prize * 1.5;break;default:break;}customer.Bill = prize;Console.WriteLine("You need pay {0} for {1}",customer.Bill,e.DishName);}}}
利用微软提供好的EventHandler委托来实现,同时将触发方法封装在内部
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EventSimply{class Program{static void Main(string[] args){Customer customer = new Customer();Waiter waiter = new Waiter();customer.order += waiter.Action;customer.Aciton();}}public class OrderEventArgs:EventArgs{public string DishName { get; set; }public string Size { get; set; }}public class Customer{public event EventHandler order;//直接使用委托,外部也能进行访问public double Bill { get; set; }public void PayBill(){Console.WriteLine("I will pay {0}",Bill);}public void Aciton() //事件永远是拥有者内部逻辑触发{this.OnOrder();}//protected访问级别保护触发方法protected void OnOrder(){if (order != null){OrderEventArgs e = new OrderEventArgs();e.DishName = "Kongpao Chicken";e.Size = "large";order.Invoke(this, e);}}}public class Waiter{internal void Action(object sender,EventArgs e){Customer customer = sender as Customer;OrderEventArgs messInfo = e as OrderEventArgs;double prize = 10;switch(messInfo.Size){case "large":prize = prize * 1.5;break;default:break;}customer.Bill = prize;Console.WriteLine("You need pay {0} for {1}",customer.Bill, messInfo.DishName);}}}

