C#学习笔记-事件

news/2024/10/5 13:57:19/文章来源:https://www.cnblogs.com/owmt/p/18194848

事件

  事件是类的一种成员,能够使类或对象具备通知能力。事件用于对象或类间的动作协调和信息传递。假设类A有某个事件,当这个事件发生时,类A会通知并传递事件参数(可选)给有订阅这个事件的类B,类B根据拿到的事件信息对事件进行响应处理。

事件模型

事件模型的5个组成部分:

  1、事件的拥有者(对象或类)

  2、事件成员(类成员)

  3、事件的响应者(对象或类)

  4、事件处理器(方法成员)

  5、事件订阅(关联事件和事件处理器),当用一个事件处理器订阅事件的时候,编译器会进行类型检查。事件和事件处理器需要遵守一个“约定”,“约定”约束了事件能够将何种消息传递给事件处理器,也约束着事件处理器能处理哪些消息。“约定”实际上指的就是委托。

  事件订阅解决了三个问题:

    1)事件发生时,事件的拥有者会通知哪些对象。

    2)使用什么样的事件处理器才能处理相应的事件。

    3)事件的响应者使用何种方法来处理事件。

//例1
class Program
{static void Main(string[] args){Timer timer = new Timer();timer.Interval = 1000;Boy boy = new Boy();Girl girl = new Girl();timer.Elapsed += boy.Action;  //两个事件处理器订阅timer.Elapsed事件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("Sing!");}
}
//例子2 事件的响应者和事件的拥有者是不同的对象
class Program
{static void Main(string[] args){Form form = new Form();Controller controller = new Controller(form);form.ShowDialog();}
}class Controller
{private Form form;public Controller(Form form){if(form != null){this.form = form;this.form.Click += this.FormClicked;}}private void FormClicked(object sender, EventArgs e){this.form.Text = DateTime.Now.ToString();}
}
//例子3 事件的响应者和事件的拥有者是同一个对象
class Program
{static void Main(string[] args){MyForm form = new MyForm();form.Click += form.Action;form.ShowDialog();}
}class MyForm : Form 
{internal void Action(object sender, EventArgs e){this.Text = DateTime.Now.ToString();}
}
//例子4 事件拥有者是事件的响应者的字段成员
class Pragram
{static void Main(string[] args){MyForm form = new MyForm();form.ShowDialog();}
}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);this.button.Click += this.ButtonClicked;  //订阅事件this.button.Text = "Say Hello";this.button.Top = 20;}private void ButtonClicked(object sender, EventArgs e)  //事件处理器{this.textBox.Text = "Hello World!";}
}

事件的声明

完整声明

  事件是基于委托的:

  1、事件需要委托类型来做约束(⭐⭐⭐);

  2、记录和保存事件处理器需要借助于委托;

  事件的本质是委托字段的包装器,对委托字段的访问起到限制作用,同时又对外界隐藏委托实例的大部分功能,仅对外暴露添加/移除事件处理器的功能

查看代码
 using System;
using System.Threading;namespace Code
{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{public string DishName { get; set; }public string Size { get; set; }}//用于声明事件的委托,约束和存储事件处理器public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);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("Customer: I will Pay ${0}", this.Bill);}public void WalkIn(){Console.WriteLine("Walk into the restaurant");}public void SitDown(){Console.WriteLine("Sit Down.");}public void Think(){for (int i = 0; i < 5; ++i) {Console.WriteLine("Let me thinking...");Thread.Sleep(1000);}if(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();}}class Waiter{public void Action(Customer customer, OrderEventArgs e){Console.WriteLine("Waiter: I will server you the dish -{0}.", e.DishName);double price = 10;if (e.Size == "small")price = price * 0.5;else if (e.Size == "large")price = price * 1.5;customer.Bill += price;}}
}

简略声明

事件的简易声明没有手动声明委托类型字段,那么事件处理器的引用存储在什么地方呢?答案是编译器会为事件准备委托类型字段存储事件处理器的引用,只不过被隐藏起来。

查看代码
 using System;
using System.Threading;namespace Code
{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{public string DishName { get; set; }public string Size { get; set; }}//用于声明事件的委托,约束和存储事件处理器public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);public class Customer{public event OrderEventHandler Order;public double Bill { get; set; }public void PayTheBill(){Console.WriteLine("Customer: I will Pay ${0}", this.Bill);}public void WalkIn(){Console.WriteLine("Walk into the restaurant");}public void SitDown(){Console.WriteLine("Sit Down.");}public void Think(){for (int i = 0; i < 5; ++i) {Console.WriteLine("Let me thinking...");Thread.Sleep(1000);}if(this.Order != null){OrderEventArgs e = new OrderEventArgs();e.DishName = "Kongpao Chicken";e.Size = "large";this.Order.Invoke(this, e);}}public void Action(){Console.ReadLine();this.WalkIn();this.SitDown();this.Think();}}class Waiter{public void Action(Customer customer, OrderEventArgs e){Console.WriteLine("Waiter: I will server you the dish -{0}.", e.DishName);double price = 10;if (e.Size == "small")price = price * 0.5;else if (e.Size == "large")price = price * 1.5;customer.Bill += price;}}
}

声明事件的委托类型的命名约定

  声明Xxx事件的委托,命名为XxxEventHandler。委托的参数有两个,一个是object类型,参数名为sender——表示事件的拥有者。另一个是EventArgs的派生类,类名一般为XxxEventArgs,参数名为e。触发事件的方法名一般为OnXxx,访问级别为protected,否则会被外界任意使用,造成滥用。

查看代码
 using System;
using System.Threading;namespace Code
{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{public string DishName { get; set; }public string Size { get; set; }}public class Customer{public event EventHandler Order;public double Bill { get; set; }public void PayTheBill(){Console.WriteLine("Customer: I will Pay ${0}", this.Bill);}public void WalkIn(){Console.WriteLine("Walk into the restaurant");}public void SitDown(){Console.WriteLine("Sit Down.");}public void Think(){for (int i = 0; i < 5; ++i) {Console.WriteLine("Let me thinking...");Thread.Sleep(1000);}this.OnOrder("Kongpao Chicken", "large");}protected void OnOrder(string dishName, string size){if (this.Order != null){OrderEventArgs e = new OrderEventArgs();e.DishName = dishName;e.Size = size;this.Order.Invoke(this, e);}}public void Action(){Console.ReadLine();this.WalkIn();this.SitDown();this.Think();}}class Waiter{public void Action(object sender, EventArgs e){Customer customer = sender as Customer;OrderEventArgs orderInfo = e as OrderEventArgs;Console.WriteLine("Waiter: I will server you the dish -{0}.", orderInfo.DishName);double price = 10;if (orderInfo.Size == "small")price = price * 0.5;else if (orderInfo.Size == "large")price = price * 1.5;customer.Bill += price;}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/739459.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将nodejs迁移到D盘

参考双击安装,指定d盘查看文件夹查看环境变量,自动添加如下Path=D:\software\nodejs\打开cmd验证C:\Users\dogle>node -v v18.14.2C:\Users\dogle>npm -v 9.5.0C:\Users\dogle>echo %PATH% C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\WINDOWS\sy…

bluez 入门

**核心模块**:位于系统的最底层,直接与蓝牙硬件进行交互。核心模块实现了蓝牙的基础协议如HCI (Host Controller Interface),并通过DBUS提供API给上层应用。 **服务守护进程(bluetoothd)**:作为系统的守护进程运行,它处理所有的蓝牙服务和配置,如设备配对、连接管理、配…

chrome 浏览器如何设置不保存表单信息

chrome 浏览器如何设置不保存表单信息如何关闭表单自动填充(测试环境Chrome浏览器) https://download.csdn.net/blog/column/7594878/100576004如何禁用谷歌浏览器中的表单自动填充功能? https://zh-cn.fairsyndication.com/222-how-to-disable-form-autofill-in-google-chr…

数字设计--CDC

异步信号与同步信号 同步信号 频率相同,相位相同的两个信号 频率相同,相位差一定的两个信号 不同频率的两个信号也可能是同步的,比如分频出来的两个信号 异步信号 频率不同的两个信号 频率相同但相位差不定的两个信号 跨时钟域就是要解决信号从异步信号之间传输的问题。 亚稳…

数字设计--亚稳态

亚稳态 FPGA 等数字设备中的所有寄存器都定义了信号时序要求----允许每个寄存器在其输入端正确捕获数据并产生输出信号。为确保可靠操作,寄存器的输入必须在时钟边沿之前的最短时间(建立时间 setup time 或 \(t_{su}\))和时钟边沿之后的最短时间(保持时间 hold time 或 \(t…

数字设计--门控时钟与时钟切换

门控时钟(ICG) 使用门控时钟的原因 芯片功耗组成中,大部分是由时钟树消耗掉的。因为这些时钟树在系统中具有最高的切换频率,而且有很多时钟buffer,并且为了最小化时钟延时,它们通常具有很高的驱动强度。此外,即使输入和输出保持不变,接收时钟的触发器也会消耗一定的功耗。…

ComfyUI进阶篇:ComfyUI核心节点(二)

ComfyUI核心节点(二) 前言: 学习ComfyUI是一场持久战。当你掌握了ComfyUI的安装和运行之后,会发现大量五花八门的节点。面对各种各样的工作流和复杂的节点种类,可能会让人感到不知所措。在这篇文章中,我们将用通俗易懂的语言对ComfyUI的核心节点进行系统梳理,并详细解释每…

题目集7~8

由于个人原因,最后两次题目集未提交,对自己的放纵感到愧疚,后期的补练我也会抽出时间完成这两道题 经过分析,题目集7,是在前面的基础上加上了一个新概念:互斥开关,以及模拟一种受控窗帘, 在我的想法,互斥开关便可当作两个普通开关去完成电路,窗帘则是新增一个用电器。…

EtherCAT转Profinet网关配置说明第一讲:配置软件安装及介绍

网关XD-ECPNS20为EtherCAT转Profinet协议网关,使EtherCAT协议和Profinet协议两种工业实时以太网网络之间双向传输 IO 数据。适用于具有EtherCAT协议网络与Profinet协议网络跨越网络界限进行数据交换的解决方案。 网关XD-ECPNS20为EtherCAT转Profinet协议网关,使EtherCAT协议…

4、flask-项目拆分

项目的拆分其实就是将app.py中的工作拆分开来、类似Django一样、每个项目都把路由模板和试图函数分开写 app.py # 路由 + 视图函数from flask import Blueprint from models import *#蓝图 # 创建蓝图对象 # 第一个参数:蓝图的名字 # 第二个参数:蓝图的包名 blue = Blueprin…

2-SAT 问题

2-SAT 问题 模型有 \(n\) 个布尔类型的变量 \(x_1, x_2, \ldots, x_n\),有 \(m\) 条限制形如 \(x_i \space [\operatorname{or}/\operatorname{and}]\space x_j=[1/0]\). 求一组符合要求的解。核心问题只需要考虑有没有解。 对于每个变量都只有两种取值:\(0/1\),那么把每…

坚果云与floccus实现Chrome书签国内跨设备、跨平台同步

本文介绍基于floccus插件与坚果云协同使用的方法,对浏览器的书签进行实时在线同步的操作~本文介绍基于floccus插件与坚果云协同使用的方法,对浏览器的书签进行实时在线同步的操作。在工作与学习中,我们时常希望在不同浏览器之间实现书签的同步;而一些传统的浏览器书签同步方…