Introduce event delegate

导论

    在学习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>

    /// 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_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>
    /// Summary description for Form1.
    /// </summary>
    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;
        /**//// <summary>
        /// Required designer variable.
        /// </summary>
        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);
        }

        /**//// <summary>
        /// Clean up any resources being used.
        /// </summary>
        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
        /**//// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        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

        /**//// <summary>
        /// The main entry point for the application.
        /// </summary>
        [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>
    /// 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);
            }
        }
    }

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

时间: 2024-09-17 05:48:15

Introduce event delegate的相关文章

一个实现自定义event的文章。。。我还没有完全摸透。。不知道有没人有兴趣。。新手就不用看了,先学会

The latest offering from Microsoft to support software development is the .NET Framework. Inside this vast Framework is the ASP.NET. The ASP.NET facilitates the application development community in generating high performance Web based applications t

[C#]Attribute特性(3)——AttributeUsage特性和特性标识符

相关文章       [C#]Attribute特性       [C#]Attribute特性(2)--方法的特性及特性参数 AttributeUsage特性      除了可以定制自己的特性来注释常用的C#类型外,您可以用AttributeUsage特性来定义您想怎样使用这些特性.AttributeUsage特性采用如下的调用惯例: 1 [AttributeUsage( 2 3 Validon, 4 5 AllowMultiple=allowmultiple, 6 7 Inherited=i

Using Attributes in C#

原文出处:http://www.codeguru.com/Csharp/Csharp/cs_syntax/attributes/article.php/c5831/ Using Attributes in C#Rating: none Sadaf Alvi (view profile)September 24, 2002 Environment: C# IntroductionAttributes are a new kind of declarative information. We can

一个SDK里做聊天室的例子(1)

聊天室 Option Explicit On Option Strict On Imports SystemImports System.IOImports System.TextImports System.ThreadingImports System.NetImports System.Net.SocketsImports System.DrawingImports System.Windows.FormsImports Microsoft.VisualBasic Class App   

C# 特性(Attribute)入门(二)

定义或控制特性的使用 AttributeUsage类是另外一个预定义特性类,它帮助我们控制我们自己的定制特性的使用.它描述了一个定制特性如和被使用. AttributeUsage有三个属性,我们可以把它放置在定制属性前面.第一个属性是: ValidOn 通过这个属性,我们能够定义定制特性应该在何种程序实体前放置.一个属性可以被放置的所有程序实体在AttributeTargets enumerator中列出.通过OR操作我们可以把若干个AttributeTargets值组合起来. AllowMul

C#里的委托和事件实现Observer

server 一.委托的简介 1.委托的声明: <access modifier> delegate <returnType> HandlerName ([parameters]) 例如: public delegate void PrintHandler(string str); 委托声明定义了一种类型,它用一组特定的参数以及返回类型来封装方法.对于静态方法,委托对象封装要调用的方法.对于实例方法,委托对象同时封装一个实例和该实例上的一个方法.如果您有一个委托对象和一组适当的参数

C#and VB.net

C#中的接口interface [public|protected|private] interface InterfaceName{ //mothed// propery// event//delegate}在实现接口时,带接口名与不带接口的区别 不带的区别eg: public interface IMyShow { void Show(); } public class MyShow:IMyShow { public void Show()//必须写上前的public若写成void Show

Using Delegates and Events 2 (来自一本49美元/817页2002年的书《C#.net web developers guide》)

guid|web Figure 2.8 ContinuedContinued.78 Chapter 2 • Introducing C# Programming// appears in the message queue. Notice the signature matches// that requried by AddEventCallbackpublic void logAddRequest( string FirstName, string LastName,string Middl

Observer模式深度探索

server [简介]微软顶级技术大师Jeffrey Richter的作品,一向是不容错过的.为了帮助开发者这篇专论Observer模式的文章也不例外.Observer模式是经典设计模式中应用最为广泛也最为灵活多变的模式之一.本文在.NET技术框架下深入发掘了Observer模式的内涵,值得细细品味. 虽然设计模式并不是万能丹,但确实是一个非常强大的工具,开发人员或架构师可使用它积极地参与任何项目.设计模式可确保通过熟知和公认的解决方案解决常见问题.模式存在的事实基础在于:大多数问题,可能已经有