C# 事件

目录

1.事件是什么?

2.事件的作用?

3.Event关键字的内部实现

4.如何使用事件?


1.事件是什么?

C# 事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。 

2.事件的作用?

C# 中使用事件机制实现线程间的通信:

  1. 类或对象可以通过事件向其他类或对象通知发生的相关事情。发送(或引发)事件的类称为"发布者",接收(或处理)事件的类称为"订阅者"。

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的两点原因:

  1. 为了子类继承。
  2. 灵活性。

定义事件的规约:事件模式要求所有事件处理程序的返回类型都是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();
}

结果:

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值