1. C#事件的概念
C# 中时间允许一个对象将发生的事件通知其他对象,将发生的事件的事件源叫发行者,通知其他的对象叫订阅者,但是订阅者可以定义也可以不定义(事件发生者也可以是事件的订阅者)
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var user = new User(); 6 user.Name = "xiaohon"; 7 user.Password = "123"; 8 9 user.EventLogin += new User.DelegateLogin(User_EventLogin); 10 user.Login(); 11 12 } 13 14 private static void User_EventLogin() 15 { 16 Console.WriteLine("login seccessful"); 17 } 18 } 19 20 class User 21 { 22 public delegate void DelegateLogin(); // 定义一个委托 23 public event DelegateLogin EventLogin; // 定义一个事件(事件用关键字加委托名来定的且后面无括号) 24 25 public string Name { get; set; } 26 public string Password { get; set; } 27 28 /// <summary> 29 /// 类的方法(可做为方法的参数即委托) 30 /// </summary> 31 public void Login() 32 { 33 Console.WriteLine("Login....."); 34 35 // 引发事件 36 if (EventLogin != null) 37 { 38 EventLogin(); 39 } 40 } 41 }
2. 事件的声明
。事件是成员,它必须声明在类或类型中,和其他成员一样。
。由于事件不是类型,我们不能像委托那样使用对象创建表达式(new 表达式)来创建它的对象。
。事件成员被隐式自动初始化为 null 。
3. 委托类型和 EventHandler
。事件声明需要委托类型的名字,我们可以声明一个委托类型或使用已存在的。声明一个委托类型,它必须指定事件保存的方法的签名和返回类型。
。更好的方案就是使用 EventHandler 委托。
4. 触发事件
。在触发事件之前和 null 进行比较,从而查看是否包含任何事件处理程序,如果事件是 null ,则表示没有。
。出发事件本身看起来像调用函数一样。
5. 订阅事件
。使用 += 运算符来添加事件处理程序(即绑定多个方法),方法可以是:
。实例方法
。静态方法
。匿名方法
。Lambda 表达式
。使用 -= 运算符来移除事件处理程序(即删除多个方法)
6. 关于 EventArg 类
。EventArgs 被设计为不能专递任何数据。它用于不需要传递数据的事件处理程序。
。通过扩展 EventArgs 来传递数据。
7. 观察者(Observer)设计模式的一种实现
热水器,加电烧水,当水温超过 95 度的时候:
1. 扬声器会开始 发出语音,告诉你水的温度;
2. 液晶屏也会改变水温的显示,来提示水已经快烧开了。
如何在水快烧开的时候通知报警器和显示器?
1. 警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)。
2. 热水器知道后保留对警报器和显示器的引用。
3. 热水器进行烧水这一动作,当水温超过 95 度时,通过对警报器和显示器的引用,自动 调用警报器的 MakeAlert()方法、显示器的 ShowMsg()方法
。Observer 设计模式:Observer 设计 模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖 于它的对象会被自动告知并更新。Observer 模式是一种松耦合的设计模式
。Observer 设计模式中主要包括如下两类对象:
。Subject:监视对象,它往往包含着其他对象所感兴趣的内容。在本范例中,热水器就是一个监视对象,它包含的其他对象所感兴趣的内容,就是 temperature 属性,当这个属性的值快到 100 时,会不断把数据发给监视它的对象。
。Observer:观察者,它监视 Subject,当 Subject 中的某件事发生的时候,会告知 Observer,而 Observer 则会采取相应的行动。在本范例中,Observer 有警报器和显示器,它们采取的行动分别是发出警报和显示水温。
常规的实现:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var heater = new Heater(); // 创建热水器对象 6 var alarm = new Alarm(); // 创建报警器对象 7 8 heater.BoilEvent += alarm.MakeAlarm; // 注册报警方法 9 heater.BoilEvent += (new Alarm()).MakeAlarm; // 给匿名对象注册方法 10 heater.BoilEvent += Display.ShowMsg; // 注册静态方法 11 12 heater.BoilWater(); // 开始烧水,根据方法的条件会自动调用注册的方法 13 14 Console.ReadKey(); 15 } 16 } 17 18 /// <summary> 19 /// 热水器 20 /// </summary> 21 class Heater 22 { 23 private int temperature; // 温度 24 public delegate void BoilHandler(int temp); // 声明委托 25 public event BoilHandler BoilEvent; // 声明事件 26 27 /// <summary> 28 /// 烧水 29 /// </summary> 30 public void BoilWater() 31 { 32 for(int i = 95; i <= 100; i++) 33 { 34 temperature = i; 35 if (temperature > 95) 36 { 37 // 如果定义的事件有对象注册 38 if(BoilEvent != null) 39 { 40 // 则执行其中的所有方法 41 BoilEvent(temperature); 42 } 43 } 44 } 45 } 46 } 47 48 /// <summary> 49 /// 温度显示器 50 /// </summary> 51 class Display 52 { 53 /// <summary> 54 /// 显示方法 55 /// </summary> 56 /// <param name="temp">温度</param> 57 public static void ShowMsg(int temp) 58 { 59 Console.WriteLine("显示器:水快烧开了,当前温度是{0}度", temp); 60 } 61 } 62 63 /// <summary> 64 /// 报警器 65 /// </summary> 66 class Alarm 67 { 68 /// <summary> 69 /// 报警方式 70 /// </summary> 71 /// <param name="temp">温度</param> 72 public void MakeAlarm(int temp) 73 { 74 Console.WriteLine("当前温度是" + temp + "度"); 75 } 76 }
8. .Net Framework 中的委托和事件
1. 委托类型的名称都应该以 EventHandler 结束。
2. 委托的原型定义:有一个 void 返回值,并接受两个输入参数:一个 Object 类型,一个 EventArgs 类型(或继承自 EventArgs)。
3. 事件的命名为委托去掉 EventHandler 之后剩余的部分。
4. 继承自 EventArgs 的类型应该以 EventArgs 结尾。
5. 委托声明原型中的 Object 类型的参数代表了 Subject ,也就是监视对象,在本例中是 Heater(热水器)。回调函数(比如 Alarm 的MakeAlert)可以通过它访问触发事件的对象(Heater)。
6. EventArgs 对象包含了 Observer 所感兴趣的数据,在本例中是 temperature。
改进后的代码:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var heater = new Heater(); // 创建热水器对象 6 var alarm = new Alarm(); // 创建报警器对象 7 8 //heater.BoilEvent += alarm.MakeAlarm; // 注册报警方法 9 //heater.BoilEvent += (new Alarm()).MakeAlarm; // 给匿名对象注册方法 10 11 heater.Boiled += Display.ShowMsg; // 注册静态方法 12 heater.Boiled += new Heater.BoilEventHandler(alarm.MakeAlarm); // 另一种注册方式 13 14 heater.BoilWater(); // 开始烧水,根据方法的条件会自动调用注册的方法 15 16 Console.ReadKey(); 17 } 18 } 19 20 /// <summary> 21 /// 热水器 22 /// </summary> 23 class Heater 24 { 25 private int _Temperature; // 温度 26 public string Brand = "格星 X-1219"; 27 public string Manufacturer = "xx 格星电器有限责任公司"; 28 29 // 委托的原型定义:有一个 void 返回值,并接受两个输入参数:一个 Object 类型, 一个 EventArgs 类型(或继承自 EventArgs) 30 public delegate void BoilEventHandler(object sender,BoiledEventArgs e); // 声明委托 委托类型的名称都应该以 EventHandler 结束 31 public event BoilEventHandler Boiled; // 声明事件 事件的命名为委托去掉 EventHandler 之后剩余的部分 32 33 /// <summary> 34 /// 构建一个可以重写的方法,以便它的派生类拒绝其他对象的访问 35 /// </summary> 36 /// <param name="e">事件参数</param> 37 protected virtual void OnBoiled(BoiledEventArgs e) 38 { 39 if(Boiled != null) 40 { 41 Boiled(this, e); 42 } 43 } 44 45 /// <summary> 46 /// 烧水 47 /// </summary> 48 public void BoilWater() 49 { 50 for(int i = 95; i <= 100; i++) 51 { 52 _Temperature = i; 53 if (_Temperature > 95) 54 { 55 BoiledEventArgs e = new BoiledEventArgs(_Temperature); 56 OnBoiled(e); 57 } 58 } 59 } 60 } 61 62 /// <summary> 63 /// 定义一个事件参数类,用于承载需要传递给 Observer 所感兴趣的信息 64 /// 继承自 EventArgs 的类型应该以 EventArgs 结尾 65 /// EventArgs 对象包含了 Observer 所感兴趣的数据,在本例中是 _Temperature 66 /// </summary> 67 public class BoiledEventArgs : EventArgs 68 { 69 public readonly int _Temperature; 70 71 public BoiledEventArgs(int temp) 72 { 73 this._Temperature = temp; 74 } 75 } 76 77 /// <summary> 78 /// 温度显示器 79 /// </summary> 80 class Display 81 { 82 /// <summary> 83 /// 显示方法 84 /// </summary> 85 /// <param name="temp">温度</param> 86 public static void ShowMsg(object sender,BoiledEventArgs e) 87 { 88 var obj = sender as Heater; 89 Console.WriteLine("显示器{0}({1})", obj.Brand, obj.Manufacturer); 90 Console.WriteLine("显示器:水快烧开了,当前温度是{0}度", e._Temperature); 91 } 92 } 93 94 /// <summary> 95 /// 报警器 96 /// </summary> 97 class Alarm 98 { 99 /// <summary> 100 /// 报警方式 101 /// </summary> 102 /// <param name="temp">温度</param> 103 public void MakeAlarm(object sender,BoiledEventArgs e) 104 { 105 Console.WriteLine("当前温度是" + e._Temperature + "度"); 106 } 107 }
参考网址:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/events/