【温故知新】c#事件event
从上一篇文章【温故知新】C#委托delegate可知,委托delegate和事件Event非常的相似,区别就是event关键字,给delegate穿上了个“马甲”。
让我们来看官方定义:
类或对象可以通过事件向其他类或对象通知发生的相关事情。 发送(或引发)事件的类称为“发行者”,接收(或处理)事件的类称为“订户”。
event 关键字用于在发行者类中声明事件。
定义非常明确,通过事件向其他类或对象通知发生的相关事情,用来实现的观察者模式。
还是通过之前的代码例子,看看声明delegate和event
//声明一个委托类型,通知家长 public delegate void NotifyDelegate(string msg); //老师被吩咐了1个委托 //声明委托:在发现早恋时时通知家长 private NotifyDelegate NotifyStudentLove; //声明事件,如果发现学生早恋! 就要通知那些订阅了这个事件的家长。 public event NotifyDelegate FindStudentLove;
让我们通过IL DASM来看看编译之后事件event的真正面目~
.event Delegate.Teacher/NotifyDelegate FindStudentLove { .addon instance void Delegate.Teacher::add_FindStudentLove(class Delegate.Teacher/NotifyDelegate) .removeon instance void Delegate.Teacher::remove_FindStudentLove(class Delegate.Teacher/NotifyDelegate) } // end of event Teacher::FindStudentLove
.method public hidebysig specialname instance void add_FindStudentLove(class Delegate.Teacher/NotifyDelegate 'value') cil managed { // 代码大小 48 (0x30) .maxstack 3 .locals init (class Delegate.Teacher/NotifyDelegate V_0, class Delegate.Teacher/NotifyDelegate V_1, class Delegate.Teacher/NotifyDelegate V_2, bool V_3) IL_0000: ldarg.0 IL_0001: ldfld class Delegate.Teacher/NotifyDelegate Delegate.Teacher::FindStudentLove IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass Delegate.Teacher/NotifyDelegate IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class Delegate.Teacher/NotifyDelegate Delegate.Teacher::FindStudentLove IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<class Delegate.Teacher/NotifyDelegate>(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: ceq IL_0028: ldc.i4.0 IL_0029: ceq IL_002b: stloc.3 IL_002c: ldloc.3 IL_002d: brtrue.s IL_0007 IL_002f: ret } // end of method Teacher::add_FindStudentLove
.method public hidebysig specialname instance void remove_FindStudentLove(class Delegate.Teacher/NotifyDelegate 'value') cil managed { // 代码大小 48 (0x30) .maxstack 3 .locals init (class Delegate.Teacher/NotifyDelegate V_0, class Delegate.Teacher/NotifyDelegate V_1, class Delegate.Teacher/NotifyDelegate V_2, bool V_3) IL_0000: ldarg.0 IL_0001: ldfld class Delegate.Teacher/NotifyDelegate Delegate.Teacher::FindStudentLove IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: ldarg.1 IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) IL_0010: castclass Delegate.Teacher/NotifyDelegate IL_0015: stloc.2 IL_0016: ldarg.0 IL_0017: ldflda class Delegate.Teacher/NotifyDelegate Delegate.Teacher::FindStudentLove IL_001c: ldloc.2 IL_001d: ldloc.1 IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<class Delegate.Teacher/NotifyDelegate>(!!0&, !!0, !!0) IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: ceq IL_0028: ldc.i4.0 IL_0029: ceq IL_002b: stloc.3 IL_002c: ldloc.3 IL_002d: brtrue.s IL_0007 IL_002f: ret } // end of method Teacher::remove_FindStudentLove
实际上编译器会帮你生成如下类似代码:
// 1. A PRIVATE delegate field that is initialized to null private EventHandler<NewMailEventArgs> NewMail = null; // 2. A PUBLIC add_Xxx method (where xxx is the Event name) // Allows objects to register interest in the event. [MethodImpl(MethodImplOptions.Synchronized)] public void add_NewMail(EventHandler<NewMailEventArgs> value) { NewMail = (EventHandler<NewMailEventArgs>) Delegate.Combine(NewMail, value); } // 3. A PUBLIC remove_Xxx method (where Xxx is the Event name) // Allows objects to unregister interest in the event. [MethodImpl(MethodImplOptions.Synchronized)] public void remove_NewMail(EventHandler<NewMailEventArgs> value) { NewMail = (EventHandler<NewMailEventArgs>) Delegate.Remove(NewMail, value); }
当一个声明delegate前添加event之后,编译器为我们封装了delegate,这样,在之后的调用,就开放了+=,-=两个方法,这样极大保证了对象安全。
我们在使用c#内置事件的时候,总会发现EventHandler,EventArgs。这是因为.NET Framework为了规范,方便开发,已经为事件发布了准则。
.NET Framework 类库中的所有事件均基于 EventHandler 委托,定义如下:
public delegate void EventHandler(object sender, EventArgs e);
让我们把上一篇的代码改为符合.NET Framework事件准则
主场景:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Delegate { class Program { static void Main(string[] args) { //家长A,B Parent pa = new Parent(); Parent pb = new Parent(); //家长A,B分别委托老师发现早恋情况时通知他们 Teacher teacher = new Teacher(); teacher.FindStudentLove += pa.ReceiveMsg; teacher.FindStudentLove += pb.ReceiveMsg; //老师开始检查早恋情况 teacher.CheckLove(); } } }
家长类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { public class Parent { /// <summary> /// 接收消息 /// </summary> /// <param name="msg">通知消息</param> public void ReceiveMsg(object sender, StudentLoveEventArgs arg) { Console.WriteLine("家长收到通知:" + arg.Message); } } }
老师类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { public class StudentLoveEventArgs : EventArgs { public StudentLoveEventArgs(string s) { message = s; } private string message; public string Message { get { return message; } set { message = value; } } } public class Teacher { //声明一个委托类型,通知家长 public delegate void NotifyDelegate(string msg); //老师被吩咐了1个委托 //声明委托:在发现早恋时时通知家长 private NotifyDelegate NotifyStudentLove; //声明事件,如果发现学生早恋! 就要通知那些订阅了这个事件的家长。 public event EventHandler<StudentLoveEventArgs> FindStudentLove; //如果还想委托老师发现学生玩手机的时候通知一声,再声明一个委托即可 private NotifyDelegate NotifyStudentPlayMobile; //封装委托,使其符合面向对象,event关键字就为我们自动封装了。 public void add_NotifyStudentLove(NotifyDelegate newdelegate) { NotifyStudentLove += newdelegate; } public void CheckLove() { //某一天AB同学突然发生纠纷!被老师发现啦! string msg = "A同学和B同学早恋啦!!"; //检查是否有人委托老师办事 if (FindStudentLove != null) { //果断通知家长 FindStudentLove(this, new StudentLoveEventArgs(msg)); } } } }
我不怕千万人阻挡,只怕自己投降。