`
gstarwd
  • 浏览: 1548244 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

C#委托,事件理解入门 (译稿)

    博客分类:
  • C#
阅读更多

在学习 C# 中的委托和事件过程中,我读了许多文章来理解他们二者究竟是怎么一回事,以及如何使用他们,现在我将整个的理解过程陈述以下,我学到的每一方面,恐怕也是你们需要掌握的 - )。

什么是委托?

   委托和事件这两个概念是完全配合的。委托仅仅是函数指针 ,那就是说,它能够引用函数,通过传递地址的机制完成。委托是一个类,当你对它实例化时,要提供一个引用函数,将其作为它构造函数的参数

每一个委托都有自己的签名(signature),例如: Delegate int SomeDelegate(string s, bool b); 是一个委托申明,在这里,提及的签名,就是说 SomeDelegate 这个委托 string bool 类型的形参,返回一个 int 类型。

上面提及的:当你对委托实例化时,要提供一个引用函数,将其作为它构造函数的参数。这里要注意了:被引用的这个函数必须和委托有相同的签名。

看下面的函数:

private int SomeFunction(string str, bool bln){...}

你可以把这个函数传给 SomeDelegate 的构造函数,因为他们有相似的签名( in other words ,他们都有相同的形参类型和个数,并且返回相同的数据类型)。

SomeDelegate sd = new SomeDelegate(SomeFunction);

sd 引用了 SomeFunction ,也就是说, SomeFunction 已被 sd 所登记注册,如果你调用 sd SomeFunction 这个函数也会被调用,记住:我所说 SomeFunction 的含义,后面,我们会用到它。

现在,你应该知道如何使用委托了,让我们继续理解事件之旅 ……

事件的理解

我们知道,在 C# 中:

l        按钮( Button )就是一个类,当我们单击它时,就触发一次 click 事件。

l        时钟( Timer )也是一个类,每过一毫秒,就触发一次 tick 事件。

让我们通过一个例子来学习,假定有这样的情节:

现在有一个 Counter 的类,它有一个方法 CountTo(int countTo, int reachableNum) ,该方法表示:在指定的时间段内( 0~~countTo ),当到达指定的时间点 reachableNum 时,就触发一次 NumberReached 事件。

它还有一个事件: NumberReached ,事件是委托类型的变量。意思是:如果给事件命名,用 event 关键字和要使用的委托类型申明它即可,如下所示:

public event NumberReachedEventHandler NumberReached;

在上面的申明中, NumberReachedEventHandle 仅是一个委托,更确切的表示应该是: NumberReachedDelegate 但是微软从不这样认为 MouseDelegate 或者 PaintDelegate ,,而是称谓: MouseEventHandler 或者 PaintEventHandler 。所以

NumberReachedEventHandler NumberReachedDelegate 听起来更方便一些, OK? 好了,让我们继续,现在你知道了,在我们声明事件之前,需要象下面这样的形式来定义委托:

public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);

现在声明的委托 NumberReachedEventHandle ,它有一个 void 返回值,和 object NumberReachedEventArgs 两个形参。就像我们在第一节中强调的那样,当实例化委托时,作为实参传入的函数也必须拥有和委托同样的签名。

在你的代码中, 你是否用过 PaintEventArgs 或者 MouseEventArgs 来确定鼠标的移动位置?是否在触发 Paint 事件的对象中用过 Graphics 属性?实际上,为用户提供数据的类都是继承于 System.EventArgs 类,就是我们常说的事件参数类,如果事件不提供参数,就不定义该类。在我们的例子中,我们通过下面的类提供预期的时间点。

public class NumberReachedEventArgs : EventArgs

{

    private int _reached;

    public NumberReachedEventArgs(int num)

    {

        this._reached = num;

    }

    public int ReachedNumber

    {

        get

        {

            return _reached;

        }

    }

}

好,有了前面的介绍,让我们到 Counter 类里面看看:

namespace Events

{

    public delegate void NumberReachedEventHandler(object sender,

        NumberReachedEventArgs e);

    /// <summary>

    /// Summary description for Counter.

    /// </summary>

    public class Counter

    {

        public event NumberReachedEventHandler NumberReached;

       

        public Counter()

        {

            //

            // TODO: Add constructor logic here

            //

        }

        public void CountTo(int countTo, int reachableNum)

        {

            if(countTo < reachableNum)

                throw new ArgumentException(

                    "reachableNum should be less than countTo");

            for(int ctr=0;ctr<=countTo;ctr++)

            {

                 if(ctr == reachableNum)

                {

                    NumberReachedEventArgs e = new NumberReachedEventArgs(

                        reachableNum);

                    OnNumberReached(e);

                    return;//don't count any more

                 }

            }

        }

        protected virtual void OnNumberReached(NumberReachedEventArgs e)

        {

            if(NumberReached != null)

            {

                NumberReached(this, e);//Raise the event

            }

        }

}

Counter 中,如果到达指定的时间点,就触发一次事件,有以下几个方面需要注意:

l        通过调用 NumberReached (它是 NumberReachedEventHandler 委托的实例)来完成一次触发事件。

NumberReached(this, e); 通过这种方式,可以调用所有的注册函数。

l        通过 NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum); 为所有的注册函数提供事件数据。

l        看了上面的代码,你可能要问了:为什么我们直接用 OnNumberReached(NumberReachedEventArgs e) 方法来调用 NumberReached this e ),而不用下面的代码呢?

    if(ctr == reachableNum)

{

    NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);

    //OnNumberReached(e);

    if(NumberReached != null)

    {

        NumberReached(this, e);//Raise the event

    }

    return;//don't count any more

}

这个问题问得很好,那就让我们再看一下 OnNumberReached 签名:

protected virtual void OnNumberReached(NumberReachedEventArgs e)

你也明白 关键字 protected 限定了 只有从该类继承的类才能调用该类中的所有方法。

关键字 virtual 表明了 在继承类中可以重写该方法。

这两点非常有用,假设你在写一个从 Counter 继承而来的类,通过重写 OnNumberReached 方法,你可以在事件触发之前,进行一次其他的工作。

protected override void OnNumberReached(NumberReachedEventArgs e)

{

    //Do additional work

    base.OnNumberReached(e);

}

注意:如果你没有调用 base.OnNumberReached(e), 那么从不会触发这个事件!在你继承该类而想剔出它的一些其他事件时,使用该方式是非常有用的。

l        还要注意到:委托 NumberReachedEventHandler 是在类定义的外部,命名空间内定义的,对所有类来说是可见的。

好,该我们来实际操作使用 Counter 类了。

在我们简单的应用程序中,我们有两个文本框,分别是: txtCountTo txtReachable

 


下面是
btnRun click 事件:

private void btnRun_Click(object sender, System.EventArgs e)

       {

           if (txtCountTo.Text == "" || txtReachable.Text=="")

              return ;

           oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));

       }

private void oCounter_NumberReached(object sender, NumberReachedEventArgs e)

       {

           MessageBox.Show("Reached: " + e.ReachedNumber.ToString());

   }

初始化事件处理的语法如下:

oCounter = new Counter();

          oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);

         

现在你明白了你刚才所做的一切,仅仅初始化 NumberReachedEventHandler 委托类型的对象(就像你实例化其他对象一样),注意到 oCounter_NumberReached 方法的签名与我前面提到的相似。

还要注意我们用的是+= 而不是= ;这是因为委托是特殊的对象,它可以引用多个对象(在这里是指它可以引用多个函数)。For example 如果有另外一个

oCounter_NumberReached 一样具有相同签名的函数oCounter_NumberReached2 ,这两个函数都可以被引用:

oCounter = new Counter();

           oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);

           oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached2);

现在,触发一个事件后,上面两个函数被依次调用。

视情况而定,如果你想让oCounter_NumberReached2NumberReached 事件发生后不再被调用,可以简单地这样写:oCounter.NumberReached -= new NumberReachedEventHandler(oCounter_NumberReached2);

最后

分享到:
评论

相关推荐

    C#委托,事件理解入门 (译稿).

    ### C#委托与事件深入解析 #### 一、委托的本质 在C#中,委托是一种类型安全的函数指针,其主要功能在于引用其他方法。它允许程序员将方法作为参数进行传递,从而实现代码的解耦和重用。具体而言,委托是一个类,...

    C#委托,事件理解入门 (译稿) - 听雨轩 - 博客园.mht

    对面向对象的心得体会。很好!希望大家可以看看,对C#的学习很有帮助。

    webots月球车仿真

    webots月球车仿真

    2025美国大学生数学建模竞赛(MCM/ICM)关键日期、规则与参赛准备指南

    内容概要:本文提供了关于2025年美国大学生数学建模竞赛的重要时间点安排以及竞赛的具体细节介绍。文中明确了从报名到最终结果发布的所有时间节点,详细列出了两种不同类型的竞赛题目特点,即传统的连续性和离散性问题和涉及数据科学技术的题目的特点。同时规定了参赛队伍构成形式、论文要求及评审标准,并提及了对工具使用的具体限制条件以及对于学术诚信的要求——强调独立解决问题的重要性。此外还简述了此次赛事所提供的多个层次获奖可能性的情况介绍。 适用人群:对数学应用有兴趣的大四本科生或者研究生;准备参加2025年度数学建模国际大赛的学生。 使用场景及目标:帮助想要参赛的同学提前规划自己的备考周期,在有限的时间内最大化提升自身的竞争力;确保选手了解完整的参赛规则避免不必要的失误导致成绩受损。 其他说明:该竞赛是一项非常具有挑战性的活动,它不仅考验了参赛者的专业知识水平,还需要他们展示出色的跨学科合作能力。因此参与者应该尽早开始准备,充分考虑各个方面的因素,如组建团队、收集资料和练习旧试题等。

    2025年最新康复医学概论考试题库与答案.docx

    2025年最新康复医学概论考试题库与答案.docx

    Move Class.html

    Move Class.html

    EXSUP2.OBJ

    C标准库源码

    2025检验类之临床医学检验技术(士)真题库附答案.pptx

    2025检验类之临床医学检验技术(士)真题库附答案.pptx

    LLSHL.OBJ

    C标准库源码

    STRREV.OBJ

    C标准库源码

    Selva Assignment Class tank problem.pptx

    Selva Assignment Class tank problem.pptx

    CHKSTK.OBJ

    C标准库源码

    基于二维四值元胞自动机和混沌系统的图像加密算法.pdf

    基于二维四值元胞自动机和混沌系统的图像加密算法.pdf

    CST闪电间接效应研究.pdf

    CST闪电间接效应研究.pdf

    智慧人防建设方案PPT(40页).pptx

    ### 智慧人防解决方案:科技赋能,守护安全 智慧人防解决方案通过整合军队、政府和社会资源,构建了一个集预警报知、指挥控制、防护救援、综合保障于一体的智能化系统。该方案依托物联网、大数据、GIS与BIM等技术,实现了对人防工程、战备物资、设备设施的全面监控与管理。通过智能监测、可视化展示、远程维护等功能,智慧人防不仅提升了人防设施的运行效率,还确保了设备的安全性和可靠性。例如,系统能够实时监测人防工程的给排水、通风、消防等设施,精准定位异常情况,确保设备始终处于最佳状态。这种“智慧化”的管理模式,不仅提高了人防系统的应急响应能力,还为战时和日常的防护救援提供了强有力的技术支撑。 ### 人防宣传教育与应急指挥:沉浸式体验与高效调度 智慧人防解决方案在人防宣传教育方面,突破了传统的展板宣传模式,引入了VR交互、移动App、微信公众号等现代化手段,打造了沉浸式的防空防灾体验平台。通过模拟空袭疏散、核武器袭击、火灾逃生等场景,市民可以在虚拟环境中学习和掌握应急技能,提升自我保护意识。此外,智慧人防还构建了应急指挥调度系统,实现了从预防、启动、处置到分析的全流程管理。系统通过视频监控、广播系统、手持终端等设备,快速获取现场信息,实时上报并指挥调度,确保突发事件能够迅速得到有效处置。这种“一键式”警报推送和逃生指示功能,不仅提高了应急响应的效率,还增强了公众的安全感。 ### 信用体系与协同监管:构建透明、高效的人防生态 智慧人防解决方案还引入了人防行业信用体系,通过公示系统、协同监管平台等工具,实现了对企业、个人和培训机构的全方位信用管理。系统支持企业信息查询、信用修复、联合惩戒等功能,确保人防行业的透明度和规范性。例如,企业可以通过平台公示年报和即时信息,公众可以查询企业的信用记录,政府部门则可以通过协同监管平台进行实时监控和数据分析。这种信用体系的建立,不仅提升了人防行业的整体管理水平,还为社会公众提供了更加安全、可靠的服务保障。通过科技手段与信用管理的结合,智慧人防解决方案为构建高效、透明的人防生态奠定了坚实基础。

    【技术分享】Python中URL处理的常见问题及解决方案,值得收藏!.docx

    【技术分享】Python中URL处理的常见问题及解决方案,值得收藏!.docx

    STRCMP.OBJ

    C标准库源码

    STRNSET.OBJ

    C标准库源码

    康复医学考试题及答案.docx

    康复医学考试题及答案.docx

Global site tag (gtag.js) - Google Analytics