目录
1.事件是什么?
C# 事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。
2.事件的作用?
C# 中使用事件机制实现线程间的通信:
- 类或对象可以通过事件向其他类或对象通知发生的相关事情。发送(或引发)事件的类称为"发布者",接收(或处理)事件的类称为"订阅者"。
3.Event关键字的内部实现
以NewMail事件为例:
public event EventHandler<NewMailEventArgs> NewMail;
C#编译器在编译时把他转化为一下3个构造:
①一个被初始化为null的私有委托字段:
private EventHandle<NewMailEventArgs> NewMail = null;
出于安全考虑,防止类外部代码不正确操作它,该字段被默认设置为private的访问权限。
②一个add_Xxx(事件名)方法:
public void add_NewMail(EventHandler<NewMailEventArgs> value)
{
//线程安全的向事件中增加订阅者
//...............
}
③一个remove_Xxx(事件名)方法:
public void remove_NewMail(EventHandler<NewMailEventArgs> value)
{
//线程安全的向事件中删除订阅者
//................
}
所以,可以说Event是对委托的一种封装,它就像一颗语法糖,不让使用者再写复杂重复的委托逻辑。
4.如何使用事件?
CLR事件模型以委托为基础。
以以下故事背景来设计运用一个事件:
第一个故事背景:
假定要设计一个电子邮件应用程序。电子邮件到达时,用户可能希望将该邮件转发给传真或寻呼机。那么我们需要做:先设计名为MailManager的类型来接收传入的电子邮件,它公开NewMail事件。其他类型(如Fax和Pager)的对象登记对该事件的关注。MailManager收到新电子邮件会引发该事件,造成邮件分发给已登记的对象。每个对象都用它们自己的方式处理邮件。
①定义类型来容纳所有需要发送给事件通知接收者的附件信息:
internal class NewMailEventArgs : EventArgs
{
private readonly String m_from, m_to, m_subject;
public NewMailEventArgs(String from, String to, String subject)
{
m_from = from;
m_to = to;
m_subject = subject;
}
public String From { get { return m_from; } }
public String To { get { return m_to; } }
public String Subject { get { return m_subject; } }
}
②定义事件成员:
事件成员使用C#关键字event定义。每个事件成员都要指定以下内容:可访问行标识符(几乎肯定是public,这样其他代码才能访问该事件成员);委托类型,指出要调用的方法的原型;以及名称。
internal class MailManager
{
public event EventHandler<NewMailEventArgs> NewMail;
}
看一下EventHandler源码:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
所以在这里的回调方法必须与EventHandler委托类型相匹配:
public void MethodName(object sender,NewMailEventArgs e);
sender是object的两点原因:
- 为了子类继承。
- 灵活性。
定义事件的规约:事件模式要求所有事件处理程序的返回类型都是void,这很有必要,因为引发事件后可能要调用好几个回调方法,但没办法获得所有方法的返回值。所以将返回类型定为void,就不允许回调返回值。
③定义负责引发事件的方法来通知事件的登记对象:
protected virtual void OnNewMail(NewMailEventArgs e)
{
EventHandler<NewMailEventArgs> temp = Volatile.Read(ref NewMail);
if (temp != null)
{
temp(this, e);
}
}
④定义方法将输入转化为期望事件:
public void SimulateNewMail(string from, string to, string subject)
{
NewMailEventArgs e = new NewMailEventArgs(from, to, subject);
OnNewMail(e);
}
⑤设计事件的订阅对象:
internal sealed class Fax
{
public Fax(MailManager mm)
{
mm.NewMail += FaxMsg;
}
private void FaxMsg(object sender, NewMailEventArgs e)
{
Console.WriteLine("Faxing mail ,message:");
Console.WriteLine($"From = {e.From},To = {e.To},Subject = {e.Subject}");
}
public void Unregister(MailManager mm)
{
mm.NewMail -= FaxMsg;
}
}
C#编译器内建了对事件的支持,会将+=、-=操作符翻译成以下代码来添加对象对事件的关注:
mm.add_NewMail(new EventHandler<NewMailEventArgs>(this.FaxMsg));
mm.remove_NewMail(new EventHandler<NewMailEventArgs>(this.FaxMsg));
⑥执行事件,并通知订阅者:
static void Main(string[] args)
{
MailManager mail = new MailManager();
Fax fax = new Fax(mail);
mail.SimulateNewMail("Cookie.K.Yang@newegg.com", "Quillan.Z.Qiu@newegg.com", "Good Bye");
Console.ReadLine();
}
结果:
你可以想象Main函数就是你在表单上输入From、To、Subject之后要点击的Click,当点击Click之后,NewMai事件的订阅者Fax收到邮件就会开始发送邮件。
第二个故事背景:
那就是通俗易懂的KeyInput事件,事件接收一个键盘输入,然后相应的订阅者去处理这个键盘输入。
①定义类型来容纳所有需要发送给事件通知接收者的附件信息:
internal class KeyEventArgs : EventArgs
{
private char keychar;
public KeyEventArgs(char keychar) : base()
{
this.keychar = keychar;
}
public char Keychar
{
get
{
return keychar;
}
}
}
②定义事件成员:
internal class KeyInputMonitor
{
public event EventHandler<KeyEventArgs> KeyDownHandler;
}
③定义方法将输入转化为期望事件:
internal class KeyInputMonitor
{
public void OnkeyInput()
{
bool finished = false;
do
{
Console.WriteLine("Input a char:");
string response = Console.ReadLine();
char responsechar = (response == "") ? ' ' : char.ToUpper(response[0]);
switch (responsechar)
{
case 'X':
finished = true;
break;
default:
KeyEventArgs keyEventArgs = new KeyEventArgs(responsechar);
KeyDownHandler(this, keyEventArgs);
break;
}
}
while (!finished);
}
}
④设计事件的订阅对象:
订阅对象1:
internal class EventReceiver1
{
public EventReceiver1(KeyInputMonitor monitor)
{
monitor.KeyDownHandler += OnKeyDown;
}
private void OnKeyDown(object sender, KeyEventArgs e)
{
Console.WriteLine($"Capture key {e.Keychar}");
}
}
订阅对象2:
internal class EventReceiver2
{
public EventReceiver2(KeyInputMonitor monitor)
{
monitor.KeyDownHandler += OnKeyDown;
}
public void OnKeyDown(object sender, KeyEventArgs e)
{
Console.WriteLine($"You enter the :{e.Keychar}");
}
}
⑤执行事件,并通知订阅者:
public class MainEntryPoint
{
public static void Start()
{
KeyInputMonitor monitor = new KeyInputMonitor();
EventReceiver1 eventReceiver = new EventReceiver1(monitor);
EventReceiver2 eventReceiver2 = new EventReceiver2(monitor);
monitor.OnkeyInput();
}
}
static void Main(string[] args)
{
MainEntryPoint.Start();
}
结果: