using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Timers; using System.Threading.Tasks; using System.Windows.Forms; namespace Event1 { #if false 1、Event的普通定义就是“能够发生什么事情, a thing that happens, especially something important!” 2、C#中的事件的定义:使对象或类具备通知能力的成员 3、作用:用于对象或类间动作协调与信息传递(消息推送) 4、原理:事件模型(也叫“发生-响应”模型) 5个部分:eg:闹钟响了你起床,可以分解为5部分:(1)闹钟(事件的发出者/拥有者 event source) (2)响铃(事件event,可以附带一些信息即事件参数) ---(3)订阅(subscribe 你关心了闹铃事件)--- (4)你(事件订阅者/响应者event subscriber) (5)起床(对事件的响应,也叫处理事件或事件处理器 event handler) 5个动作:(1)我有一个事件 (2)一个人或一群人关心我这个事件 (3)我这个事件发生了 (4)关心这个事件的人会依次得到通知 (5)收到通知的人根据拿到的事件信息(又称事件参数/数据)对事件进行响应(处理事件) 5、常用场景 GUI编程 MVC MVP MVVM是事件模型的升级 绝大部分情况下都不需要自己定义事件,都是使用预定义的事件 6、事件和委托的关系 委托只是事件和事件处理器之间的的一种调用约定 #endif class Program { static void Main(string[] args) { // 类最重要的三个部分(存储数据、做事情、通知别人) // 属性:表示当前对象的状态 // 事件:表示对象能在什么情况下通知谁 // 方法:表示当前对象能做什么 #if true // 模型1 *:(事件的拥有者、事件) 和 (事件订阅者、事件处理器) 两大部分分别独立,这是最基础的模型,也是MVC这些模式的雏形 // event source System.Timers.Timer timer = new System.Timers.Timer(); // Forms命名空间里面也有Timer,这里是为了解决冲突写明确指明是System.Timers下的Timer timer.Interval = 1000; // 1000ms // event subscribers Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); // event --- +=(subscribe) --- event handler timer.Elapsed += sub1.TimerEventHandler; timer.Elapsed += sub2.TimerEventHandler; // event source开始工作,每隔1000ms发出Elapsed事件通知 timer.Start(); #endif #if true // 模型2 ***:(事件订阅者、事件处理器) 内部包含 (事件的拥有者、事件) ,这是典型的GUI中常用模型, // 比如 主窗口(按钮点击事件的订阅者)里面有一个按钮控件(event source) 订阅(subscribe) 按钮的点击事件(event) 并且拥有按钮点击事件的处理方法(event handler) MainForm mainForm = new MainForm(); mainForm.ShowDialog(); #endif #if false // 模型3 **:事件订阅者和拥有者是同一个对象,事件 和 事件处理器都属于该对象,即自己使用自己的事件处理器处理自己的事件 MyForm form = new MyForm(); form.Click += form.FormClicked; form.ShowDialog(); #endif Console.ReadLine(); } } class Subscriber1 { internal void TimerEventHandler(object sender, ElapsedEventArgs e) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Subscriber1 handing timer's elapsed event!"); } } class Subscriber2 { internal void TimerEventHandler(object sender, ElapsedEventArgs e) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Subscriber2 handing timer's elapsed event!"); } } class MainForm : Form { public MainForm() { this.Text = "MainForm"; this.Width = 400; this.Height = 300; this.FormBorderStyle = FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.textBox = new TextBox(); this.textBox.Top = 5; this.textBox.Width = this.Width - 10; this.textBox.Height = 25; this.button = new Button(); this.button.Top = 10 + this.textBox.Height; this.button.Width = this.Width - 10; this.button.Text = "Button1"; this.oldButton = new Button(); this.oldButton.Top = this.button.Top + this.button.Height + 5; this.oldButton.Width = this.Width - 10; this.oldButton.Text = "Button2"; this.Controls.Add(this.textBox); this.Controls.Add(this.button); this.Controls.Add(this.oldButton); // MainForm 订阅 button成员 的 Click 事件 安装的事件处理器为 ButtonClicked this.button.Click += this.ButtonClicked; // 一些事件处理器的旧式写法即显式使用委托的方式 //this.oldButton.Click += new EventHandler(OldButton_Click); // EventHandler是一个委托类型,现在直接传一个方法给它也将自动生成对应的委托,就跟上面那样 //this.oldButton.Click += delegate (object sender, EventArgs e) //{ // // 这说白了就是个匿名委托 // this.textBox.Text = "MainForm's Button2 Clicked!"; //}; // 新式的Lambda表达式,甚至可以不写参数的类型,编译器会根据Click事件处理器对应的委托约束 (选中Click按下F12可以跳转) 来正确推导类型 this.oldButton.Click += (object sender, EventArgs e) => { this.textBox.Text = "MainForm's Button2 Clicked!"; }; } private void OldButton_Click(object sender, EventArgs e) { this.textBox.Text = "MainForm's Button2 Clicked!"; } // 按钮点击事件的处理器 private void ButtonClicked(object sender, EventArgs e) { this.textBox.Text = "MainForm's Button1 Clicked!"; } private TextBox textBox; private Button button; // 事件的拥有者event source,它是事件订阅者MainForm的一个成员 private Button oldButton; } class MyForm : Form { internal void FormClicked(object sender, EventArgs e) { MessageBox.Show("Form Clicked!"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Event2 { class Program { static void Main(string[] args) { // 事件拥有者 Customer customer = new Customer(); // 事件订阅者 Water water = new Water(); // 订阅事件、添加事件处理器 customer.Order += water.Action; // customer.Order.Invoke // error 这就是为什么需要事件的原因,如果Order是一个委托,则它可以在外面任意调用,但是事件类型在外部是只能出现在 += 左边,不允许其他操作 // 此时如果是委托,那么别的一个顾客就可以帮一个不认识的顾客瞎点菜,即 customer2.Order.Invoke(customer, e); // 由事件拥有者在某些条件下触发事件 customer.Action(); customer.PayTheBill(); } } public class OrderEventArgs : EventArgs { public string DishName { get; set; } public string Size { get; set; } } // 约束事件和事件处理器的委托类型(即约束返回值、参数) public delegate void OrderEventHandler(Customer sender, OrderEventArgs e); // 事件拥有者 public class Customer { #if false // 完整声明形式: // 用于保存事件处理器(处理器也是受 OrderEventHandler 委托类型约束) private OrderEventHandler orderEventHandler; // 声明事件(使用 OrderEventHandler 委托类型作为约束条件) public event OrderEventHandler Order { add { this.orderEventHandler += value; // 添加事件处理器 } remove { this.orderEventHandler -= value;// 删除事件处理器 } } #endif // 事件的简单声明形式(编译器在后台自动创建了OrderEventHandler委托类型的字段) public event OrderEventHandler Order; public double Bill { get; set; } public void PayTheBill() { Console.WriteLine("Pay the bill ${0}", this.Bill); } public void WalkIntoRestaurant() { Console.WriteLine("Customer walk into the restaurant..."); } public void SitDown() { Console.WriteLine("Customer sit down..."); } public void BrowseDishMenu() { Console.ForegroundColor = ConsoleColor.Yellow; for (int i = 0; i < 5; ++i) { Console.WriteLine("Browsing and choosing..."); Thread.Sleep(1000); } Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Choose ok! Need a water..."); #if false // 使用完整声明时: // 判断是否有事件处理器 if (this.orderEventHandler != null) { OrderEventArgs e = new OrderEventArgs() { DishName = "Kongpao Chicken", Size = "normal" }; this.orderEventHandler.Invoke(this, e); // 触发事件 } #endif // 使用简单声明时 if (this.Order != null) { OrderEventArgs e = new OrderEventArgs() { DishName = "Kongpao Chicken", Size = "normal" }; this.Order.Invoke(this, e); // 触发事件 } } public void Action() { WalkIntoRestaurant(); SitDown(); BrowseDishMenu(); } } // 事件订阅者 public class Water { public void Action(Customer customer, OrderEventArgs e) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("Water: a new dish:{0}", e.DishName); double price = 10.0; switch (e.Size) { case "small": price = price * 0.5; break; case "normal": price = price * 1.0; break; case "big": price = price * 1.5; break; default: break; } customer.Bill += price; } } }