首页 后端开发 C#.Net教程 C#委托,事件理解入门

C#委托,事件理解入门

Dec 21, 2016 pm 02:54 PM

目录 

l        导论 

l        什么是委托 

l        事件的理解 

l        事件 关键字 

l        最后 

  

导论 

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

什么是委托? 

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

  每一个委托都有自己的签名,例如: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 description for Counter. 

    ///
 

    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_NumberReached2在NumberReached事件发生后不再被调用,可以简单地这样写:oCounter.NumberReached -= new NumberReachedEventHandler(oCounter_NumberReached2); 

  

最后   让我们看一下完整的源代码,以供参考: 


Form1.cs 


using System; 
using System.Drawing; 
using System.Collections; 
using System.ComponentModel; 
using System.Windows.Forms; 
using System.Data; 

namespace Events 

    /**////  
    /// Summary description for Form1. 
    ///
 
    public class Form1 : System.Windows.Forms.Form 
    { 
        Counter oCounter = null; 

        private System.Windows.Forms.Button cmdRun; 
        private System.Windows.Forms.TextBox txtReachable; 
        private System.Windows.Forms.TextBox txtCountTo; 
        private System.Windows.Forms.Label label1; 
        private System.Windows.Forms.Label label2; 
        private System.Windows.Forms.Button btnRemoveDelegate; 
        /**////  
        /// Required designer variable. 
        ///
 
        private System.ComponentModel.Container components = null; 

        public Form1() 
        { 
            // 
            // Required for Windows Form Designer support 
            // 
            InitializeComponent(); 

            // 
            // TODO: Add any constructor code after InitializeComponent call 
            // 
            oCounter = new Counter(); 
            oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached); 
            oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached2); 
        } 

        /**////  
        /// Clean up any resources being used. 
        ///
 
        protected override void Dispose( bool disposing ) 
        { 
            if( disposing ) 
            { 
                if (components != null)  
                { 
                    components.Dispose(); 
                } 
            } 
            base.Dispose( disposing ); 
        } 

        Windows Form Designer generated code#region Windows Form Designer generated code 
        /**////  
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor. 
        ///
 
        private void InitializeComponent() 
        { 
            this.cmdRun = new System.Windows.Forms.Button(); 
            this.txtReachable = new System.Windows.Forms.TextBox(); 
            this.txtCountTo = new System.Windows.Forms.TextBox(); 
            this.label1 = new System.Windows.Forms.Label(); 
            this.label2 = new System.Windows.Forms.Label(); 
            this.btnRemoveDelegate = new System.Windows.Forms.Button(); 
            this.SuspendLayout(); 
            //  
            // cmdRun 
            //  
            this.cmdRun.Location = new System.Drawing.Point(16, 72); 
            this.cmdRun.Name = "cmdRun"; 
            this.cmdRun.Size = new System.Drawing.Size(48, 23); 
            this.cmdRun.TabIndex = 2; 
            this.cmdRun.Text = "Run"; 
            this.cmdRun.Click += new System.EventHandler(this.cmdRun_Click); 
            //  
            // txtReachable 
            //  
            this.txtReachable.Location = new System.Drawing.Point(144, 40); 
            this.txtReachable.Name = "txtReachable"; 
            this.txtReachable.Size = new System.Drawing.Size(56, 20); 
            this.txtReachable.TabIndex = 1; 
            this.txtReachable.Text = ""; 
            //  
            // txtCountTo 
            //  
            this.txtCountTo.Location = new System.Drawing.Point(144, 16); 
            this.txtCountTo.Name = "txtCountTo"; 
            this.txtCountTo.Size = new System.Drawing.Size(56, 20); 
            this.txtCountTo.TabIndex = 0; 
            this.txtCountTo.Text = ""; 
            //  
            // label1 
            //  
            this.label1.AutoSize = true; 
            this.label1.Location = new System.Drawing.Point(16, 16); 
            this.label1.Name = "label1"; 
            this.label1.Size = new System.Drawing.Size(51, 13); 
            this.label1.TabIndex = 3; 
            this.label1.Text = "Count To"; 
            //  
            // label2 
            //  
            this.label2.AutoSize = true; 
            this.label2.Location = new System.Drawing.Point(16, 40); 
            this.label2.Name = "label2"; 
            this.label2.Size = new System.Drawing.Size(99, 13); 
            this.label2.TabIndex = 4; 
            this.label2.Text = "Reach this number"; 
            //  
            // btnRemoveDelegate 
            //  
            this.btnRemoveDelegate.Location = new System.Drawing.Point(16, 104); 
            this.btnRemoveDelegate.Name = "btnRemoveDelegate"; 
            this.btnRemoveDelegate.Size = new System.Drawing.Size(168, 23); 
            this.btnRemoveDelegate.TabIndex = 5; 
            this.btnRemoveDelegate.Text = "Remove second handler"; 
            this.btnRemoveDelegate.Click += new System.EventHandler(this.btnRemoveDelegate_Click); 
            //  
            // Form1 
            //  
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); 
            this.ClientSize = new System.Drawing.Size(224, 134); 
            this.Controls.AddRange(new System.Windows.Forms.Control[] { 
                                                                          this.btnRemoveDelegate, 
                                                                          this.label2, 
                                                                          this.label1, 
                                                                          this.txtCountTo, 
                                                                          this.txtReachable, 
                                                                          this.cmdRun}); 
            this.Name = "Form1"; 
            this.Text = "Events"; 
            this.ResumeLayout(false); 

        } 
        #endregion 

        /**////  
        /// The main entry point for the application. 
        ///
 
        [STAThread] 
        static void Main()  
        { 
            Application.Run(new Form1()); 
        } 

        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()); 
        } 
        private void oCounter_NumberReached2(object sender, NumberReachedEventArgs e) 
        { 
            MessageBox.Show("Reached2: " + e.ReachedNumber.ToString()); 
        } 

        private void btnRemoveDelegate_Click(object sender, System.EventArgs e) 
        { 
            oCounter.NumberReached -= new NumberReachedEventHandler(oCounter_NumberReached2); 
            oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text)); 
        } 
    } 

  






Counter.cs 


  

using System; 

namespace Events 

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

    /**////  
    /// Summary description for Counter. 
    ///
 
    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); 
            } 
        } 
    } 

    public class NumberReachedEventArgs : EventArgs 
    { 
        private int _reached; 
        public NumberReachedEventArgs(int num) 
        { 
            this._reached = num; 
        } 
        public int ReachedNumber 
        { 
            get 
            { 
                return _reached; 
            } 
        } 
    } 

 以上就是C#委托,事件理解入门的内容,更多相关内容请关注PHP中文网(www.php.cn)! 


本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

C语言各种符号的使用方法 C语言各种符号的使用方法 Apr 03, 2025 pm 04:48 PM

C 语言中符号的使用方法涵盖算术、赋值、条件、逻辑、位运算符等。算术运算符用于基本数学运算,赋值运算符用于赋值和加减乘除赋值,条件运算符用于根据条件执行不同操作,逻辑运算符用于逻辑操作,位运算符用于位级操作,特殊常量用于表示空指针、文件结束标记和非数字值。

char在C语言字符串中的作用是什么 char在C语言字符串中的作用是什么 Apr 03, 2025 pm 03:15 PM

在 C 语言中,char 类型在字符串中用于:1. 存储单个字符;2. 使用数组表示字符串并以 null 终止符结束;3. 通过字符串操作函数进行操作;4. 从键盘读取或输出字符串。

char在C语言中如何处理特殊字符 char在C语言中如何处理特殊字符 Apr 03, 2025 pm 03:18 PM

C语言中通过转义序列处理特殊字符,如:\n表示换行符。\t表示制表符。使用转义序列或字符常量表示特殊字符,如char c = '\n'。注意,反斜杠需要转义两次。不同平台和编译器可能有不同的转义序列,请查阅文档。

c#多线程和异步的区别 c#多线程和异步的区别 Apr 03, 2025 pm 02:57 PM

多线程和异步的区别在于,多线程同时执行多个线程,而异步在不阻塞当前线程的情况下执行操作。多线程用于计算密集型任务,而异步用于用户交互操作。多线程的优势是提高计算性能,异步的优势是不阻塞 UI 线程。选择多线程还是异步取决于任务性质:计算密集型任务使用多线程,与外部资源交互且需要保持 UI 响应的任务使用异步。

char与wchar_t在C语言中的区别 char与wchar_t在C语言中的区别 Apr 03, 2025 pm 03:09 PM

在 C 语言中,char 和 wchar_t 的主要区别在于字符编码:char 使用 ASCII 或扩展 ASCII,wchar_t 使用 Unicode;char 占用 1-2 个字节,wchar_t 占用 2-4 个字节;char 适用于英语文本,wchar_t 适用于多语言文本;char 广泛支持,wchar_t 依赖于编译器和操作系统是否支持 Unicode;char 的字符范围受限,wchar_t 的字符范围更大,并使用专门的函数进行算术运算。

char在C语言中如何进行类型转换 char在C语言中如何进行类型转换 Apr 03, 2025 pm 03:21 PM

在 C 语言中,char 类型转换可以通过:强制类型转换:使用强制类型转换符将一种类型的数据直接转换为另一种类型。自动类型转换:当一种类型的数据可以容纳另一种类型的值时,编译器自动进行转换。

char和unsigned char的区别是什么 char和unsigned char的区别是什么 Apr 03, 2025 pm 03:36 PM

char 和 unsigned char 是存储字符数据的两种数据类型,主要区别在于处理负数和正数的方式:值范围:char 有符号 (-128 到 127),unsigned char 无符号 (0 到 255)。负数处理:char 可以存储负数,unsigned char 不能。位模式:char 最高位表示符号,unsigned char 无符号位。算术运算:char 和 unsigned char 作为有符号和无符号类型,其算术运算方式不同。兼容性:char 和 unsigned char

C语言中char的常见错误及避免方法 C语言中char的常见错误及避免方法 Apr 03, 2025 pm 03:06 PM

C语言中char的使用错误和避免方法:未初始化char变量:使用常量或字符串文字初始化。超出字符范围:比较变量值是否在有效范围内(-128 到 127)。字符比较不区分大小写:使用toupper()或tolower()转换字符大小写。使用char*引用字符数组时未加'\0':使用strlen()或手动添加'\0'标记数组结尾。使用char数组时忽略数组大小:明确指定数组大小或使用sizeof()确定长度。使用char指针时未检查空指针:使用前检查指针是否为NULL。使用char指针指向非字符数据

See all articles