zoukankan      html  css  js  c++  java
  • [C#基础] 委托

    什么是委托

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

    使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为此变量代表一个方法),可以依次调用所有绑定的方法。

    委托的定义

     通常一个委托被定义为

     public delegate void CalcDelegate(int x, int y);

    关键字delegate用来声明一个委托类型CalcDelegate,可以对其添加访问修饰符,默认返回类型为void,接受两个int型参数x和y,但是委托并不等于方法,而是一个引用类型。

    下面介绍如何通过委托来实现一个计算器模拟程序。

            //声明一个委托
            public delegate void CalcDelegate(int x, int y);
            //创建与委托关联的方法,两者具有相同的返回值类型和参数列表
            public static void Add(int x, int y)
            {
                Console.WriteLine(x+y);
            }
            //定义委托类型变量
            private static CalcDelegate MyDelegate;
            static void Main(string[] args)
            {
               //进行委托绑定
                MyDelegate = new CalcDelegate(Add);
                //回调Add方法
                MyDelegate(1, 2);
            }

    上述示例,在类内部声明了一个CalcDelegate委托类型,它具有和关联方法Add完全相同的返回值类型和参数列表,否则将导致编译错误。将方法Add传递给CalcDelegate构造器,也就是将方法Add指派给CalcDelegate委托,并将该引用赋给MyDelegate变量,也就表示MyDelegate变量保存了指向Add方法的引用,以此实现对Add 的回调。

    由此可见,委托表示了对其回调方法的签名,可以将方法当做参数进行传递,并根据传入的方法来动态改变方法的调用。只要为委托提供相同签名的方法,就可以与委托绑定。

    例如:

         public static void Subtract(int x, int y)
            {
                Console.WriteLine(x - y);
            }
         static void Main(string[] args)
            {
               //进行委托绑定
                MyDelegate = new CalcDelegate(Subtract);
                //回调Add方法
                MyDelegate(1, 2);
            }

    多播委托和委托链

    在上述委托实现中,Add方法和Subtract方法可以绑定与同一个委托类型MyDelegate,由此可以想到将多个方法绑定到一个委托变量,在调用一个方法时,可依次执行其绑定的所有方法,这称为多播委托。

    在.NET 中 以 += 和 -= 操作符分别进行绑定和解除绑定的操作,多个方法绑定到一个委托变量就形成了一个委托链,对其调用时将会依次调用所有绑定的回调方法。

    例如:

        static void Main(string[] args)
            {
               //进行委托绑定
                MyDelegate = new CalcDelegate(Add);
                MyDelegate += new CalcDelegate(Subtract);
                //回调方法
                MyDelegate(1, 2);
            }

     上述执行将在控制台依次输出 3和-1,可见多播委托按照委托链顺序调用所有绑定的方法,同样以 -=操作可以解除委托链上的绑定。

    事实上, +=和-=分别调用了 Delegate.Combine和  Delegate.Remove方法

    所以上述示例等效于:

           static void Main(string[] args)
            {
                //进行委托绑定
                MyDelegate = (CalcDelegate)Delegate.Combine(new CalcDelegate(Add), new CalcDelegate(Subtract));
                MyDelegate(1, 2);
    
            }

     另外多播委托返回值一般为void值,委托类型为非void类型时,多播委托将返回最后一个调用方法的执行结果,所以在实际应用中不被推荐!

    委托的本质

    委托在本质上仍然是一个类

     

    该类继承自System.MulticastDelegate类,该类维护一个带有链接的委托列表,在调用多播委托时,将按照委托列表顺序而调用的。还包括一个接受两个参数的构造函数,和三个重要方法 BeginInvoke,EndInvoke和Invoke

    在IL代码中可见,首先通过构造函数来创建一个MyDelegate实例,然后通过CalcDelegate::Invoke 执行回调方法调用,可见真正执行调用的是Invoke 方法。因此,也可以通过Invoke 在代码中显示调用。

     MyDelegate.Invoke(1, 2);

    匿名方法

    匿名方法以内联方式放入委托对象的使用位置,而避免创建一个委托来关联回调方法,也就是由委托调用了匿名的方法

    下述示例用匿名方法的方式来实现上述的示例

                CalcDelegate myAddDelegate = delegate(int x, int y)
                {
                    Console.WriteLine(x + y);
                };
    
                CalcDelegate mySubstractDelegate = delegate(int x, int y)
                {
                    Console.WriteLine(x - y);
                };
    
                myAddDelegate(1, 2);
                mySubstractDelegate(2, 1);

    事实上匿名方法和委托在IL上是等效的,编译器为匿名方法两个静态成员和静态方法

    Lambda表达式

    C#3.0引入了Lambda表达式,简化了匿名方法的语法。 我们很容易通过如下步骤把匿名方法转换为lambda表达式。

    • 删除delegate关键字
    • 在参数列表和匿名方法主体之间放lambda运算符 =>

    下述示例演示了lambda表达式

           CalcDelegate myAddDelegate = (int x, int y) =>
                {
                    Console.WriteLine(x + y);
                };
    
                CalcDelegate mySubstractDelegate =(int x, int y)=>
                {
                    Console.WriteLine(x - y);
                };

    编译器通过推断,允许我们更进一步简化lambda表达式

    • 编译器可以从委托的声明中知道委托类型的参数,因此lambda允许我们省略类型参数
    • 如果只有一个隐式类型参数,可以省略圆括号
    • 如果语句块包含一个返回语句,可以将语句块替换为return关键字后的表达式

    有关lambda参数列表的要点如下

    • lambda表达式参数列表的参数必须在参数数量,类型和位置上与委托相匹配
    • 表达式的参数列表中的参数不一定需要包含类型,除非委托有ref或out参数
    • 如果只有一个参数,并且是隐式参数,圆括号可以省略
    • 如果没有参数,必须使用空的圆括号()
  • 相关阅读:
    c++ list_iterator demo
    模板元编程例子
    !a && !b 和 !(a || b) 的故事
    简明解释算法中的大O符号
    重构oceanbase的一个函数
    正则表达式识别汉字
    编写易于理解代码的六种方式
    Linux下的tar压缩解压缩命令详解
    2013 年 —— Facebook 在开源方面的工作介绍
    Kent Beck揭秘Facebook开发部署流程
  • 原文地址:https://www.cnblogs.com/Nomads/p/4881978.html
Copyright © 2011-2022 走看看