在C#的事件、多播中使用委托

    (1)委托是什么?

    1.委托在C#中可以看作是对象的一种新类型(委托是一种在对象里保存方法引用的类型)。委托把一个方法作为参数传入到另一个方法,通过传递地址的机制完成。它相当于C/C++的函数指针,不过它是类型安全的函数指针。委托可以拥有多个方法。

    2.委托是派生于基类System.Delegate的一个类的实例.

    3.我们一般在函数中的参数传递的都是数据,但是如果我们需要在函数中的参数传递的是方法,那么我们就需要使用委托。由于方法执行的操作并不是对数据进行操作,而是要对另外一个方法进行操作。在编译时我们不知道第二个方法是什么,这个信息只能在运行时得到。[1]当要把方法传给其他方法时,需要使用委托。

    4.委托处理的问题在C++中可以用函数指针处理,而在Java中则可以用接口处理。它通过提供类型安全和支持多方法改进了函数指针方式;它通过可以进行方法调用而不需要内部类适配器或额外的代码去处理多方法调用问题而改进了接口方式。[2]

    (2)如何使用委托?

    1. 定义委托

        定义前加关键字delegate,因为定义委托基本上是定义一个新类,所以可以在定义类的的任何地方定义委托,  也可以在任何类的外部定义。还可以在名称空间中把委托定义为顶层对象。根据定义的可见性,可以在委托上添加一般的访问修饰符。[1]
    格式:
    <access modifier> delegate <returntype> handlername ([parameters])
    举例:
    delegate double DoubleOp(double x);


    2.使用委托

     在C#中,委托在语法上总是带有一个参数函数,这个参数就是委托应用的方法。委托所使用的方法必须和委托声明相一致,即参数列表、返回值都一致。

    委托的实例可以表示任何类型的任何对象的实例方法或者任何类型的静态方法。
    以下两个例子分别说明:
    (1)委托的实例可以表示任何类型的任何对象的实例方法。
    //创建一个对象
    MathOperations instanceExmaple = new MathOperations();

    //将该对象的方法作为参数传给委托
    DoubleOp instanceExmapleMethod = new DoubleOp(instanceExmaple.MultiplyByTwo);

    (2)委托的实例可以表示任何类型的静态方法。
    //将类的静态方法作为参数传给委托
    DoubleOp staticExmapleMethod = new DoubleOp(MathOperations.DivideByThree);

    3.完整的举例如下:

    using System;

    namespace DeleGate
    {
 
     class MathOperations
     {  
      //乘以2
      public double MultiplyByTwo(double firstArg)
      {
       return firstArg * 2;
      }

      //除以3
      public static double DivideByThree(double firstArg)
      {
       return firstArg / 3;
      }
     } 

     class MathExample
     {
      delegate double DoubleOp(double x);
 
      static void Main(string[] args)
      {
       MathOperations instanceExmaple = new MathOperations();//创建一个对象
       DoubleOp instanceExmapleMethod = new DoubleOp(instanceExmaple.MultiplyByTwo);//将该对象的方法作为参数传给委托

       DoubleOp staticExmapleMethod = new DoubleOp(MathOperations.DivideByThree);//将类的静态方法作为参数传给委托

       double fstResult,scdResult;
            
       //以下两种方法的结果是一样的,instanceExmaple.MultiplyByTwo(5.0)就相当于instanceExmapleMethod(5.0)
       fstResult = instanceExmaple.MultiplyByTwo(5.0);//使用对象的方法
       scdResult = instanceExmapleMethod(5.0);//使用委托

       //打印上面的结果
       Console.Write("instanceExmaple.MultiplyByTwo Result:" +  fstResult.ToString());
       Console.WriteLine();
       Console.Write("instanceExmapleMethod Result:" + scdResult.ToString());
       Console.WriteLine();

       //以下两种方法的结果是一样的, MathOperations.DivideByThree(6.0)就相当于staticExmapleMethod(6.0)
       fstResult = MathOperations.DivideByThree(6.0);//使用类的静态方法
       scdResult = staticExmapleMethod(6.0);//使用委托

       打印上面的结果
       Console.Write("MathOperations.DivideByThree Result:" + fstResult.ToString());
       Console.WriteLine();
       Console.Write("staticExmapleMethod Result:" + scdResult.ToString());
       Console.WriteLine();
   
      }
     }
    }

    (3)关于委托和多播


      多播的委托包含对两个或者两个以上方法的引用。

      多播的委托使用+= 来添加委托,使用-=来移除委托。多播的委托包含的方法必须返回void,否则会抛出run-time exception。因为方法返回的不是void,那么不知道使用众多方法的返回结果中的哪一个了。另外,使用多播时,方法中不能包含 out 参数。

      多播委托中的方法是按顺序被调用的,所以各个方法应该没有任何依赖关系.否则,需要自己保证调用次序。

    1. 定义委托
    delegate void DoubleOp(double x);

    2.使用多播的委托
    DoubleOp instanceExmapleManyMethods = instanceExmapleMethod + staticExmapleMethod;//将两个委托加入到一个多播的委托中
    instanceExmapleManyMethods(3.0);//使用多播的委托

    3.完整的例子:

    using System;

    namespace DeleGate
    {
     delegate void Delegate_Multicast(int x, int y);

     class MathOperations
     {  
      //乘以2
      public void voidMultiplyByTwo(double firstArg)
      {
       double result = firstArg * 2;  
       Console.Write("voidMultiplyByTwo:" + result.ToString());
       Console.WriteLine();
      }

      //除以3
      public static void voidDivideByThree(double firstArg)
      {
       double result =  firstArg / 3;
       Console.Write("voidDivideByThree:" + result.ToString());
       Console.WriteLine();
      }
     }

     class MathExample
     {
      delegate void DoubleOp(double x);
   
      static void Main(string[] args)
      {
       MathOperations instanceExmaple = new MathOperations();//创建一个对象
       DoubleOp instanceExmapleMethod = new DoubleOp(instanceExmaple.voidMultiplyByTwo);//将该对象的方法作为参数传给委托

       DoubleOp staticExmapleMethod = new DoubleOp(MathOperations.voidDivideByThree);//将类的静态方法作为参数传给委托

       DoubleOp instanceExmapleManyMethods = instanceExmapleMethod + staticExmapleMethod;//将两个委托加入到一个多播的委托中
       instanceExmapleManyMethods(3.0);//使用多播的委托
       instanceExmapleManyMethods -= instanceExmapleMethod;//移出多播的委托中的一个委托
       instanceExmapleManyMethods(3.0);//使用多播的委托
     
      }
     }
    }


    (4)关于委托和事件

   委托支持事件处理,可以将事件处理过程注册给委托。触发事件就调用委托。

   什么是事件?在C#中,事件就是当对象发生某些事情时,类向该类的客户提供通知的一种方法。[3]举个例子;按钮(Button)就是一个类,当我们单击它时,就触发一次click事件,它通过按钮所以在的类,我被单击了,然后调用相应的方法来处理。

    整个使用过程如下:
 
   (1) 事件的声明格式:<access modifier> event <delegate type> eventname

   根据声明格式中的<delegate type>,所以我们必须先声明该事件的委托类型。例如,在.Net Framework中关于委托类型EventHandler的声明就如下:
   [C#]
   [Serializable]
   public delegate void EventHandler(
       object sender,
      EventArgs e
   );


  其中第一个参数指明了触发该事件的对象即sender,由于引发事件的对象不可预知的,因此我们把其声明成为object类型,所有的对象都适用。第二个参数(e)包含了在事件处理函数中可以被运用的一些数据,各种类型的事件中可能不同,这要根据类中事件成员的说明决定。[5]“e”参数的类型应为eventargs 类或派生自 eventargs 类。[4]

   (2)我们声明该委托类型的事件,举例如下: 
    [C#]
    public event EventHandler Click;

    类声明了事件以后,可以就像处理所指示的委托类型的字段那样处理该事件。如果没有任何客户将委托与该事件绑定,则该字段将为空;否则该字段引用应在调用该事件时调用的委托。因此,调用事件时通常先检查是否为空,然后再调用事件。(调用事件,即触发事件,只能从声明该事件的类内进行)[4]
  
   (3)对用户控件的按钮注册事件,将委托与该事件绑定.
   this.Button1.Click += new System.EventHandler(this.Button1_Click);//将该方法绑定到Click事件上

    this.Button1.Click -= new System.EventHandler(this.Button1_Click);//将已绑定的该方法从到Click事件上解除

    事件处理函数的参数形式必须和委托对象的参数形式相一致。[5] 

    我们先简单的看一下 Button 用户控件类与委托和事件相关的部分代码,它能让我们更加深入的了解在控件中使用委托和事件的。

    public delegate void EventHandler(object sender,EventArgs e);

    public class Button : Control
    {
        public event EventHandler Click;

        protected void OnClick(EventArgs e)
         {
                if (Click != null) Click(this, e);
        }

        public void Reset()
        {
                Click = null;
        }
    }

(以上红色部分的内容为06年08月07号追加)

    (4)举一个ASP.Net中常见的例子:

    private void InitializeComponent()
    { 
    
        this.Button1.Click += new System.EventHandler(this.Button1_Click);
    
    }

    private void Button1_Click(object sender, System.EventArgs e)
    {
        //完成相应的操作
    }

    这就是用委托来实现事件.[3]

   在该例子中,我们没有声明委托,也没有通过event关键字来引用该委托对象,因为在.Net Framework已经替我们完成了这两项工作。我在(1)、(2)中的举例就来自.Net Framework。
   这个例子常常在ASP.Net中的用户自定义控件的事件中使用。
  

    (5)什么情况下使用委托

    1.委托支持事件处理

    我们在用户控件中对事件做出响应时,需要使用委托。

    2.委托支持多播

    完成一连串的任务时,可以使用多播。

    (6)为什么说委托的类型是安全的。

      在我们刚才的[(4)关于委托和事件]中,我们是这样事件中是这样使用委托的:
    this.Button1.Click += new System.EventHandler(this.Button1_Click);

    那么,我们为什么不直接把让事件调用方法: 
    this.Button1.Click =  this.Button1_Click;

      这是因为面向对象编程时,方法很少孤立存。在调用前,通常需要与类的实例相关联,上面直接的调用就没有考虑这个问题。所以C#语法不允许直接使用这种方法。如果要传递方法,就需要把方法的细节包装到一个中新类型的对象中,即委托。委托只是一种特殊的对象类型,其特殊之处在于我们以前定义的对象都包含数据,而委托只包含方法的细节。[1](举的例子如果不恰当请指出!:))

本文引用:

[1] 《C#高级编程》第二版 第253页~第262页 清华大学出版社
[2] http://www.soft6.com/know/detail.asp?id=BACEGH
[3] http://bokegu.com/forums/987/PrintPost.aspx
[4] http://www.a2605.org/ddvipcom/program/c-/index11/79.htm
[5] http://www.cublog.cn/opera/showart.php?blogid=13205&id=70291

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值