`

事 件

    博客分类:
  • CLR
 
阅读更多

现在让我们来讨论类型中可定义的最后一种成员:事件

类型之所以能提供事件通知功能,是因为类型维护了一个已登记方法的列表。事件发生后,类型将通知列表中所有已登记的方法。

CLR的事件模型建立在委托的基础上。

现在我们来描述一个场景,一个电子邮件系统,当电子邮件到达时,可以将该邮件转发给传真机处理。因此,我们需要先设计一个MailManager类,它负责接受传入的邮件,它维护了一个已登记方法的列表,它公开了一个名为NewMail的事件。还要设计一个Fax类,它的方法可登记或取消登记对这个NewMail事件的关注。当MailManager收到一封电子邮件时,会引发NewMail事件,然后它将该邮件发给Fax,Fax将会按自己的方法处理邮件。

 

设计MailManager这样一个公开了事件的类需要经过多个步骤②③④

①定义类型来容纳所有需要发送给事件通知接受者(Fax)的附加信息

事件引发时,引发事件的对象可能希望向接受事件的通知的对象传递一些附加的信息。这些附加信息需要封装到它自己的类中,这个类通常包含一组私有字段,以及一些用于公开这些字段的只读公共属性。根据约定,这种类应该从System.EventArgs派生,并且类名应该以EventArgs结束。

 

 internal class NewMailEventArgs:EventArgs

    {

        private 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; } }

    }

 

 internal class MailManager

    {

        ②定义事件成员

        几乎肯定是public,这样其他代码才能访问该事件成员;event关键字;一个委托类型,它指出要调用方法的原型; 一个事件名称

        public event EventHandler<NewMailEventArgs> NewMail;

        EventHandler<NewMailEventArgs>意味着Fax必须提供一个方法和EventHandler<NewMailEventArgs>委         托类型匹配的回调方法,由于泛型System.EventHandler委托类型的定义如下:

        public delegate void EventHandler<TEventArgs>(Object sender,TEventArgs e)         where TEventArgs:EventArgs;

        所以方法原型必须具有以下形式:

        void MethodName(Object sender,NewMailEventArgs e)    lookhere16

        ③定义负责引发事件的方法来通知事件的登记对象

        根据约定,还需要定义一个受保护的虚方法。要引发事件时,当前类及其派生类中的代码会调用该方法。该方法要获 取一个参数,也就是一个NewMailEventArgs对象。该方法的默认实现只是检查一下是否有对象登记了对事件的关         注。如果有,就引发事件,从而通知事件的登记对象。

         protected virtual void OnNewMail(NewMailEventArgs e)

        {

            EventHandler<NewMailEventArgs> temp = System.Threading.Interlocked.CompareExchange(ref NewMail, null, null);

            if (temp != null) { temp(this, e);}

        }

        ④如果需要获取一些用户输入(这里是from,to,subject),可以定义一个方法调用OnNewMail方法从而引发事件

        public void WriteMail(string from, string to, string subject)

        {

            NewMailEventArgs e = new NewMailEventArgs(from, to, subject);

            OnNewMail(e);

        }

    }

 

public event EventHandler<NewMailEventArgs> NewMail;

C#编译器在编译以上一行代码时,会把它转换为以下3个构造

private EventHandler<NewMailEventArgs> NewMail=null;

public void add_NewMail(EventHandler<NewMailEventArgs> value){...}

public void remove_NewMail(EventHandler<NewMailEventArgs> value){...}

 

设计侦听事件的类型Fax

  internal class Fax

    {

        public void register(MailManager mm)

        { 

            mm.NewMail+=doFax;

        }

        public void unregister(MailManager mm)

        {

            mm.NewMail -= doFax;

        }

        public void doFax(object sender,NewMailEventArgs e) {

            System.Windows.Forms.MessageBox.Show(e.From+" "+e.To+" "+e.Subject+" "+sender.GetType().ToString());

        }

    }

 

在Form中测试

 public partial class Form1 : Form

    {

        MailManager mm = new MailManager();

        Fax fax = new Fax();

        public Form1()

        {

            InitializeComponent();

        }

        private void btnRegister_Click(object sender, EventArgs e)

        {

            fax.register(mm);

        }

        private void btnUnregister_Click(object sender, EventArgs e)

        {

            fax.unregister(mm);

        }

        private void btnSend_Click(object sender, EventArgs e)

        {

            string from= txtFrom.Text;

            string to=txtTo.Text;

            string subject = txtSubject.Text;

            mm.WriteMail(from,to,subject);

        }

    }

先点击btnRegister(关注事件)再点击Send,Fax会收到通知    先点击btnUnregister(取消关注)再点击Send,Fax不会收到通知

 

拿Control类型来讲,它大概定义了70个事件,如果每个事件都是像上面那样实现,那么每个Control对象将会包含70个委托,所以在创建Control对象时这会浪费大量的内存。所以微软通过一个System.ComponentModel.EventHandlerList类型来解决这个问题。现在就让我们写一个类似EventHandlerList的类型--EventSet:

之前先创建一个EventKey类:

这个类的目的是在使用EventSet时,提供多一点的类型安全性和代码可维护性

public sealed class EventKey{}

public sealed class EventSet

    {

        私有字典用于维护 EventKey --> Delegate 映射

        private readonly Dictionary<EventKey, Delegate> m_events = new Dictionary<EventKey, Delegate>();

        添加一个EventKey --> Delegate 映射(如果EventKey不存在),或者将一个委托与一个现有的EventKey合并

        public void Add(EventKey eventkey, Delegate handler) {

            System.Threading.Monitor.Enter(m_events);

            Delegate d;

            m_events.TryGetValue(eventkey, out d);

            m_events[eventkey] = Delegate.Combine(d, handler);

            System.Threading.Monitor.Exit(m_events);

        }

        从EventKey(如果它存在)删除一个委托,并且在删除最后一个委托时删除EventKey --> Delegate 映射

        public void Remove(EventKey eventkey, Delegate handler) {

            System.Threading.Monitor.Enter(m_events);

            Delegate d;

            if (m_events.TryGetValue(eventkey,out d))

            {

                d = Delegate.Remove(d, handler);

                如果还有委托,就设置新的头部(地址),否则删除EventKey

                if (d != null)

                {

                    m_events[eventkey] = d;

                }

                else

                {

                    m_events.Remove(eventkey);

                }

            }

        }

        为指定的EventKey引发事件

        public void Raise(EventKey eventkey, object sender, EventArgs e) { 

        如果EventKey不在集合中,不抛出一个异常

            Delegate d;

            System.Threading.Monitor.Enter(m_events);

            m_events.TryGetValue(eventkey, out d);

            System.Threading.Monitor.Exit(m_events);

            if (d!=null)

            {

                由于字典可能包含几个不同的委托类型,所以无法在编译的时候构造一个类型安全的委托调用。

                因此我调用System.Delegate类型的DynamicInvoke方法,以一个对象数组的形式向它传递回调方法的参数。

                在内部DynamicInvoke会向调用的回调方法查证参数的类型安全性,并调用方法。如果存在类型不匹配的情况,DynamicInvoke会抛出一个异常。

                d.DynamicInvoke(new object[] { sender, e });

            }

        }

    }

我们再用这个EventSet来实现MailManager,现在我们叫它MailManager2:

 internal class MailManager2

    {

        private readonly EventSet m_eventset = new EventSet();

        public EventSet Eventset

        {

            get { return m_eventset; }

        }

        protected static readonly EventKey m_eventkey = new EventKey();

        public event EventHandler<NewMailEventArgs> NewMail {

            add { m_eventset.Add(m_eventkey, value); }

            remove { m_eventset.Remove(m_eventkey, value); }

        }

        protected virtual void OnNewMail(NewMailEventArgs e) {

            m_eventset.Raise(m_eventkey, this, e);

        }

        public void WriteMail(string from, string to, string subject)

        {

            NewMailEventArgs e = new NewMailEventArgs(from, to, subject);

            OnNewMail(e);

        }

    }

在Form中测试

 public partial class Form1 : Form

    {

        MailManager2 mm = new MailManager2();

        Fax fax = new Fax();

        public Form1()

        {

            InitializeComponent();

        }

        private void btnRegister_Click(object sender, EventArgs e)

        {

            fax.register(mm);

        }

        private void btnUnregister_Click(object sender, EventArgs e)

        {

            fax.unregister(mm);

        }

        private void btnSend_Click(object sender, EventArgs e)

        {

            string from= txtFrom.Text;

            string to=txtTo.Text;

            string subject = txtSubject.Text;

            mm.WriteMail(from,to,subject);

        }

    }

会看到相同的效果

分享到:
评论

相关推荐

    金融工程专题报告:重大事 件期权波动率策略复盘.pdf

    金融工程专题报告:重大事件期权波动率策略复盘。在重大事件前夕,采取中性对冲策略,做空或者做多波动率来避免指数的大幅度波动造成的损失。

    每天程序员必做8件事

    每天程序员必做8件事 每天程序员必做8件事 每天程序员必做8件事 每天程序员必做8件事 每天程序员必做8件事 每天程序员必做8件事

    软件架构师应该知道的97件事.pdf

    软件架构师作为一个专业领域的高级职位,其核心职能...《软件架构师应该知道的97件事》这本书通过多位专家的经验分享,提供了一个关于如何成为优秀软件架构师的丰富知识库,对于提升软件架构师的专业能力具有重要价值。

    一件开心的事作文.pdf

    一件开心的事作文.pdf

    java基础入门教程

    了 用 Java编写 的 软 件 产 品 ,受 到 工 业 界的 重 视 与 好 评 ,认 为 "Java是八 十 年 代 以 来 计 算 机 界 的 一 件 大 事 ", 微 软 总 裁 比 尔 ·盖 茨 在 悄 悄 地 观 察了 一 段 时 间 后 ,不 无感 慨 ...

    九宫件事图形化软件

    九宫件事图形化软件

    开学初班主任要做好六件事.pdf

    开学初班主任要做好六件事

    三年级作文学写一件事或一件难忘的事PPT教案.pptx

    这篇PPT教案主要针对三年级学生的作文教学,旨在帮助学生学习如何描述一件事或一件难忘的事,以提高他们的叙事写作能力。以下是一些关键知识点: 1. **叙事文的基本要素**:在教学过程中,强调了记叙文的六要素,即...

    装完系统后必须做的10件事

    【标题】:“装完系统后必须做的10件事” 【主要内容】:在完成新系统的安装后,为了确保系统的稳定性和安全性,以下10个步骤至关重要: 1. 不要立即接入网络:新安装的系统可能存在未修补的安全漏洞,容易受到...

    程序员都知道那点97件事儿!97.Things.Every.Programmer.Should.Know.Feb.2010.pdf

    97件事儿》,由Kevlin Henney编辑,首次出版于2010年2月。该书汇集了来自众多国外软件开发领域专家的智慧和经验,包含了97条建议,旨在帮助程序员在日常工作中提升能力、避免常见错误并不断进步。 根据提供的内容...

    电脑题库.pdf

    * 信息熵是事 件发生不确定性的度量,当熵的值越大时,说明事 件发生的不确定性越大。 四、计算机系统结构 * 诺依曼体系结构计算机图灵机是冯·诺依曼提出的计算机体系结构模型。 五、数据表示 * 在计算机内,二...

    Java中有关Null的9件事

    Java中有关Null的9件事,里面对null有了详细的介绍。

    世界上最美的10件事ppt模板.rar

    世界上最美的10件事:初吻,看日出,与很在乎的那个人牵手,躺在床上听屋外的雨声,每次看到那个人心里七上八下的感觉,深夜不眠与室友聊天,一个信任的眼神,睡觉的时候、太阳照在身上,一觉醒来发现还可以睡上几个...

    最成功与最失败的一件事.doc

    最成功与最失败的一件事 摘要:本文讲述了作者的两件经历,一件是成功通过英语六级考试和书面语精品级考试,另一件是失败的经验教训,包括一次考试的失败和一份工作竞赛的失败。作者总结了失败的经验,并将其转化为...

    小学作文难忘的一件事PPT学习教案.pptx

    这篇PPT学习教案主要针对小学阶段的作文教学,旨在帮助学生掌握如何撰写“难忘的一件事”这类主题的作文。从内容来看,它包含了以下几个重要的知识点: 1. **叙事文的基本结构**:文中强调了叙述一件事时应包含的...

    新手主播开播前,必须知道的这6件事.pdf

    新手主播开播前,必须知道的这6件事

    IT运维服务管理和保障体系.pdf

    该流程首先是新建事 件,在运维平台中,新建事 件有两个来源:一是当用户发现信息系统故障,通过电话或邮件通知服务台,由服务台人员在系统中手动创建事 件;二是当监控平台通过监视工具发现性能指标超过闭值时,...

    童年趣事、一件趣事、一件事或一件有趣的事等开头和结尾.doc

    童年趣事、一件趣事、一件事或一件有趣的事等开头和结尾.doc

Global site tag (gtag.js) - Google Analytics