位置:海鸟网 > IT > ASP.NET >

详解asp.net中的委托

本文将详细讲解asp.net中的委托

1.1.1 定义

  委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,如下面的示例所示:

  //Code in C#

  public delegate int PerformCalculation(int x, int y);

  与委托的签名(由返回类型和参数组成)匹配的任何方法都可以分配给该委托。

  简单理解Delegate委托(或代理)是一种数据类型:它的变量可以引用到某一个符合要求的方法上,通过委托可以间接地调用该方法。

  其实.NET的委托类似于C语言的函数指针,区别在于.NET委托是类型安全的,这说明,C中的函数指针只不过是一个指向存储单元的指针,我们无法说出这个指针实际指向什么。

1.1.2 委托使用
  • 使用委托的四部曲:
  • 定义一种委托类型

  • 委托执行时要调用方法

  • 定义一个委托实例

  • 委托实例的调用

  •   我们先定义一种委托类型如下:

    delegate GetStringLength(

      大家猜猜看哪个和上面提供的委托类型签名匹配(签名匹配:参数类型,参数个数和返回类型匹配)。激动时刻到了马上公布答案,和委托类型匹配的方法是PrintString和PrintObject,如果有不明白的请细细考虑一下委托匹配的条件—签名匹配。

                                         

    图1委托成功输出

     

      现在对委托有了一定的认识,接下来我们将介绍委托最经常使用的地方—事件。

      我们将从发送器和接受器的角度讨论事件,例如在UI编程中,鼠标单击或键盘按键,发送器就是.NET的CLR,注意事件发送器并不知道接收器是谁,这符合面向对象的原则,而且某个事件接收器有个方法处理该事件,这个时候就要委托,如前面所讲事件发送器对事件接收器一无所知,通过委托作为一个中介,接收器把事件处理方法注册到事件中,这样就实现了由发送器->委托->接收器的过程了。

      我们可以这样认为:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

      

    1.1.3 自定义委托

      前面话有点难以理解,接下来我们通过具体的例子分析一下何谓委托,该如何实现委托。现在不是很喜欢搞多国语言化的吗?看看如何让我们的程序会说多种语言吧!

    ///<summary>
    /// the English speaker.
    ///</summary>
    ///public name)
    {
    Console.WriteLine(
    , name));
    }

    ///<summary>
    /// the Chineses speaker.
    ///</summary>
    public)
    {
    Console.WriteLine(

      好啦现在我们有两个方法分别是说普通话和英语,现在我们的程序会说普通话和英语啦。现在我们考虑究竟什么时候讲普通话什么时候讲英语,那不简单我们加个判断就OK啦,是的我们可以通过switch或者if else就可以实现啦。

    ///<summary>
    /// 根据上下文调用不同的方法
    ///</summary>
    ///
    ///privatestatic name, Language lang)
    {
    switch (lang)
    {
    case Language.Chinese:
    Program.ChineseSpeaker(name);
    break;
    case Language.English:
    Program.EnglishSpeaker(name);
    break;
    default :
    break;
    }
    }

      但假设我们现在又要增加新的语言西班牙语,同样我们可以增加西班牙语,但我们必须修改switch语句增加判断,这不符合OOP中的OCP(对扩展开放,对修改关闭原则),这时候委托该登场。

    ///<summary>
    /// Define speak delegate.
    ///</summary>
    ///<param name="name"></param>
    privatedelegate

      

      首先我们定义了一种委托类型SpeakDelegate,然后我们通过修改Say方法看看该如何使用委托变量。

    ///<summary>
    /// The base say function.
    ///</summary>
    ///
    ///privatestatic name, SpeakDelegate speaker)
    {

      现在我们的参数已经不是枚举类型了,而是一个委托类型变量,而且实现的具体代码也没有了switch语句了,比之前简单了许多。现在大家知道如何去调用Say方法吧!没错我们只需传递一个name和一个具体实现函数名就OK了。

      自定义委托相信大家都会了,接下来我将介绍一下.NET中委托实现,由于许多使用委托的例子都是事件,所以下面的例子也采用事件。但请大家要注意“可以使用委托,但却没有定义事件”的情况(例如:回调函数)。

    1.1.4 .NET中的事件委托

      举一个简单的例子,.NET中经常使用的控件Button,当我们把Button 控件 drap and drop到界面,然后双击界面的Button我们发现程序中自动生成了一个响应Button的事件方法,然后我们给事件方法添加Code之后,当我们点击该Button就响应该方法了,但我们没有看到代码中有任何的委托和事件之类的定义,其实这些.NET都已经做好了。我们可以查看如下文件。

                  图2事件委托实现

      如上图所示我们打开Designer文件,事件委托的实现都在这里实现了。

    其中,EventHandler就是一个代理类型,可以认为它是一个“类”,是所有返回类型为void,具备两个参数分别是object sender和EventArgs e,第一个参数表示引发事件的控件,或者说它表示点击的那个按钮。通过以下的代码我们细细解析一下。

    private sender, EventArgs e)
    {
    Button;
    true;
    }
    else
    {

     

     

     

     

     

    图3点击产生效果

      

      OK现在明白了sender就是传递一个被点击对象的实例,第二个参数名叫e的EventArgs参数,用于      表示附加的事件关联的事件信息。当点击按钮时,没有附加任何关联的事件信息,如上的点击事件,第二参数并不表示任何有用的信息。但什么时候会用到呢?

      我们先介绍一下EventArgs这个的类型。其实这个类并没有太多的功能,它主要是作为一个基类让其他类去实现具体的功能和定义,当我们搜索EventArgs发现很多类是继承于它的。

    publicclass EventArgs
    {
    publicstaticreadonly EventArgs Empty;

    static EventArgs();
    public EventArgs();
    }

      举其中的ImageClickEventArgs为例,它继承于EventArgs,而且还添加了自己的字段用来基类X和Y的坐标值(这是一个ImageButton被点击时候响应的),然后获取该按钮的X和Y坐标。

    publicsealedclass ImageClickEventArgs : EventArgs
    {
    publicint X;
    publicint Y;

    y)
    {
    x;

    protected sender, ImageClickEventArgs e)
    {
    e.X.ToString();

     

    图4获取ImageClickEventArgs关联点击坐标

     

      前面提到其他事件关联信息类型都是通过继承EventArgs实现的,所以说我们自己也可以自定义一个事件关联信息类型,如下我们只需继承EventArgs就OK了。

    ///<summary>
    /// 自定义事件关联类
    ///</summary>
    publicclass ColorChangedEventArgs : EventArgs
    {
    private Color color;

    ///<summary>
    class.
    ///</summary>
    /// c;
    }

    ///<summary>
    /// Gets the color of the get.
    ///</summary>
    ///<value>
    /// The color of the get.
    ///</value>
    public Color GetColor
    {

    1.1.5自定义事件委托

    多播委托

      前面使用的每个委托都只包含一个方法调用。调用一个委托就调用一个方法调用。如果要通过一个委托调用多个方法,那就需要使用委托的多播特性。如果调用多播委托,就可以按委托添加次序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果,接下来看看多播实现。

    namespace Multi_Delegate
    {
    delegatevoid StringProcessor();
    publicclass Person
    {
    privatestring _Name;
    name)
    {
    name;
    }

    public._Name);
    }

    public._Name);
    }
    }

    class Program
    {
    static

      也许有人觉得很简单,实现的确简单明了,就是通过“+”把方法调用绑定到委托变量中,如果我们用“-”就可以移除绑定到委托变量方法了。

     事件

      前面一直没有解释什么是事件,现在让我用一句话解释事件和委托的关系吧!

      事件和委托关系就像是属性和字段的关系,为了刚好的实现OOP的编程原则,事件对委托进行了封装。

      现在我们修改前面的代码,使用事件对委托进行封装。

    /// 使用事件对委托进行封装
    ///</summary>
    publicclass Say
    {
    ///<summary>
    /// 封装委托字段
    ///</summary>
    publicstaticevent SpeakDelegate speakDelegate;

    ///<summary>
    /// 调用委托具体实现方法
    ///</summary>
    ///<param name="name"></param>
    publicstatic name)
    {
    speakDelegate(name);
    }
    }


    ///<summary>
    /// 客户端调用委托
    ///</summary>
    ///<param name="args"></param>
    static

    图8GoF观察者架构

    namespace GoFObserver
    {
    ///<summary>
    /// 充当Subject角色
    ///</summary>
    publicclass GofTelecom
    {
    publicdelegatevoid GofNews();
    publicstaticevent GofNews NewEvent;

    ///<summary>
    /// 发布通知方法
    ///</summary>
    ///<returns></returns>
    publicstaticbool Notify()
    {
    null)
    {
    NewEvent();
    returnfalse;
    }
    returntrue;
    }
    }

    publicinterface IObserver
    {
    void Update();
    }

    ///<summary>
    /// 观察者
    ///</summary>
    publicclass Programmer : IObserver
    {

    #region IObserver 成员

    public);
    }

    #endregion

    }

    ///<summary>
    /// 观察者
    ///</summary>
    publicclass Architect : IObserver
    {
    #region IObserver 成员

    public);
    }

    #endregion
    }



    publicclass Program
    {
    static Architect());
    ob.Update;
    }

    );
    }