【温故知新】c#事件event

时间:2021-08-07 16:39:38

从上一篇文章【温故知新】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
.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.
IL_0001: ldfld class Delegate.Teacher/NotifyDelegate Delegate.Teacher::FindStudentLove
IL_0006: stloc.
IL_0007: ldloc.
IL_0008: stloc.
IL_0009: ldloc.
IL_000a: ldarg.
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.
IL_0016: ldarg.
IL_0017: ldflda class Delegate.Teacher/NotifyDelegate Delegate.Teacher::FindStudentLove
IL_001c: ldloc.
IL_001d: ldloc.
IL_001e: call !! [mscorlib]System.Threading.Interlocked::CompareExchange<class Delegate.Teacher/NotifyDelegate>(!!&,
!!,
!!)
IL_0023: stloc.
IL_0024: ldloc.
IL_0025: ldloc.
IL_0026: ceq
IL_0028: ldc.i4.
IL_0029: ceq
IL_002b: stloc.
IL_002c: ldloc.
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
.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.
IL_0001: ldfld class Delegate.Teacher/NotifyDelegate Delegate.Teacher::FindStudentLove
IL_0006: stloc.
IL_0007: ldloc.
IL_0008: stloc.
IL_0009: ldloc.
IL_000a: ldarg.
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.
IL_0016: ldarg.
IL_0017: ldflda class Delegate.Teacher/NotifyDelegate Delegate.Teacher::FindStudentLove
IL_001c: ldloc.
IL_001d: ldloc.
IL_001e: call !! [mscorlib]System.Threading.Interlocked::CompareExchange<class Delegate.Teacher/NotifyDelegate>(!!&,
!!,
!!)
IL_0023: stloc.
IL_0024: ldloc.
IL_0025: ldloc.
IL_0026: ceq
IL_0028: ldc.i4.
IL_0029: ceq
IL_002b: stloc.
IL_002c: ldloc.
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));
}
} }
}