C#复习笔记-事件
使用委托时,一般会出现两种角色,广播者和订阅者。广播者是包含委托字段的类型,它通过委托决定何时进行广播。订阅者是方法的接收者。它通过在广播者的委托上调用+=或者-=来决定何时开始监听何时结束监听。事件是一种使用委托的功能实现广播者或订阅者模型的结构。使用委托的主要目的是保证订阅者之间互不影响。声明事件的最简单的方法是在委托成员前面加上event关键字:
//定义一个委托,接受2个decimal类型的参数 public delegate void PirceChangedHandler(decimal oldPirce, decimal newPrice); public class Broadcaster { //定一个PirceChanged事件 public event PirceChangedHandler PriceChanged; decimal _price; public decimal Price { get { return _price; } set { if (_price == value) return; decimal oldprice = _price; _price = value; if (PriceChanged != null) PriceChanged(oldprice, _price); } } }
EventArgs是为事件传递信息的基类。我们可以继承EventArgs以便于在事件触发的时候传递值参数。EventArgs子类应当根据它包含的信息来命名,而非根据使用它的事件命名。一般将数据以属性或制度字段的方式暴露给外界。
1 2 3 4 5 6 7 8 9 10 11 | public class PriceChangedEventArgs : EventArgs { public readonly decimal Price; public readonly decimal OldPrice; public PriceChangedEventArgs( decimal price, decimal oldPrice) { Price = price; OldPrice = oldPrice; } } |
定义了EvaenArgs子类以后,下一步就要定义事件的委托了,委托必须满足下面三个条件:
- 委托必须以void作为返回值。
- 委托必须接受两个参数。第一个参数是object类型,第二个参数是EventArgs子类。第一个参数表明了时间的广播者,第二个参数包含了需要传递的信息。
- 委托的名称必须以EventHandler结尾。如下:
1 | public delegate void PriceChangedEventHandler( object sender, PriceChangedEventArgs e); |
框架定义了一个名为System.EventHandler<>的泛型委托也满足以上三个条件,事件模式还需要编写一个protected的虚方法来触发事件,方法名和时间名一致,以On为前缀,接收唯一的EvenArgs参数:
1 2 3 4 5 6 7 8 9 10 11 | public class Stock { public event EventHandler<PriceChangedEventArgs> PriceChanged; protected virtual void OnPriceChanged(PriceChangedEventArgs e) { var temp = PriceChanged; //为了多线程下可靠的工作,在测试和调用委托之前需要将他保存到零时变量 if (temp != null ) temp( this , e); PriceChanged?.Invoke( this , e); //也可以使用null条件运算符来避免声明临时变量,这种写法是最好的事件触发方式 } } |
完整的代码如下:
public class PriceChangedEventArgs : System.EventArgs { public readonly decimal LastPrice; public readonly decimal NewPrice; public PriceChangedEventArgs(decimal lastPrice, decimal newPrice) { LastPrice = lastPrice; NewPrice = newPrice; } } public class Stock { public event EventHandler<PriceChangedEventArgs> PriceChanged; protected virtual void OnPriceChanged(PriceChangedEventArgs e) { //为了多线程下可靠的工作,调用委托之前需要将他保存到零时变量 //var temp = PriceChanged; //if (temp != null) // temp(this, e); PriceChanged?.Invoke(this, e);//也可以这么写,线程安全又书写简单,是最好的触发方式。 } decimal price; public decimal Price { get { return price; } set { if (price == value) return; decimal priceold = price; price = value; OnPriceChanged(new PriceChangedEventArgs(priceold, price)); } } } using App; Stock stock = new Stock(); stock.Price = 20; stock.PriceChanged += Stock_PriceChanged; for (int i = 0; i < 10; i++) { stock.Price = i + 20; Task.Delay(1000).Wait(); } void Stock_PriceChanged(object? sender, PriceChangedEventArgs e) { Console.WriteLine($"当前价格:{e.NewPrice},上一次的价格{e.LastPrice}"); } Console.ReadKey();
和方法一样,事件也可以重写,可以是虚的,抽象的密封的,静态的。
从别后, 忆相逢, 几会魂梦与汝同。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端