委托delegate的声明和使用、匿名方法、Lambda表达式

概述:委托和类一样,是一种用户自定义的类型,但类表示的是数据和方法的集合,而委托则持有一个或多个方法,以及一系列预定义操作,C#中委托类似于C++中的函数指针,委托是存有对某个方法引用的一种引用型变量,引用可在运行时被改变,委托常用于实现事件和回调方法,所有委托都派生自System.Delegate类。

    delegate void MyDel(int value);  //声明委托类型
    class Program
    {
        public void foo(int i)
        {
            Console.WriteLine("小于50");
        }
        public void fun(int i)
        {
            Console.WriteLine("大于50");
        }
        static void Main(string[] args)
        {
            Program p = new Program();
            MyDel del;  //声明委托变量
            Random rand = new Random();
            int value = rand.Next(100);  //得到一个0~100之间的一个随机数
            Console.WriteLine(value);
            del = value < 50 ? new MyDel(p.foo) : new MyDel(p.fun);
            del(value);   //执行委托
        }
    }

声明委托类型

可以把delegate看做一个包含有序方法列表的对象,这些方法具有相同的签名和返回类型,方法的列表被称为调用列表,委托保存的方法可以来自任何类或结构,只要他们的委托的返回类型和委托的签名(包括ref和out修饰符)相同,调用列表中的方法可以是实例方法也可以是静态方法在调用委托的时候,会执行其调用列表中的所有方法

创建委托对象的俩种方式

委托是引用类型,因此有引用和对象,有俩种创建委托对象的方式,一种是带有new运算符的方式,一种是不带的。以MyDel

为委托类型为例。

方式1:MyDel del=new MyDel(a.foo);
方式2:MyDel del=a.foo;

方式2使用快捷语法,它仅由方法说明符构成,这种快捷语法能够工作是因为在方法名称和其相应的委托类型之间存在隐式转换

组合委托

委托可以使用额外的运算符来组合,这个运算最终会创建一个新的委托,其调用列表连接了作为操作数的俩个委托的调用列表副本

为委托添加方法(+=运算符),为委托增减方法也叫委托的多播(组播)

使用+=运算符可以为委托添加方法,由于委托是不可变的,所以使用+=添加方法本质上不是在原有的委托上进行添加,而是重新生成一个有3个方法的全新委托。然后将这个新的委托值赋值给原引用。且方法可以重复添加,这样调用列表中就会出现多个相同的方法,在调用的时候也会被依次进行多次调用

为委托移除方法(-=运算符)

使用-=运算符从委托移除方法,与增加委托方法一样,其实是创建了一个新的委托,新的委托是旧委托的副本,只是没有了已经被移除方法的引用。

移除委托的注意事项:
(1)如果在调用列表中的方法有多个实例,-=运算符将从列表最后开始搜索,并且移除第一个与方法匹配的实例
(2)试图删除委托中不存在的方法没有效果
(3)试图调用空委托会抛出异常,可以通过委托和null进行比较来判断委托的调用列表是否为空,如果调用列表为空,则委托是null。

    delegate void MyDel(int value);  //声明委托类型
    class Program
    {
        public void foo(int i)
        {
        }
        static void Main(string[] args)
        {
            Program p = new Program();
            MyDel del;  //声明委托变量
            del = p.foo;   //赋值
            del -= p.foo;   //移除
            if (del == null)  //判断是否为空委托
                Console.WriteLine("空委托"); //空委托
        }
    }

调用带返回值、带引用参数的委托

返回值:如果委托有返回值并且在调用列表中有多个方法,则调用列表中最后一个方法(指最后添加到列表中的方法)返回的值就是委托调用的返回值,调用列表中所有其他方法的返回值都会被忽略

引用参数:如果委托有引用参数,参数值会根据调用列表中的一个或多个方法的返回值而改变,在调用委托列表中的下一个方法时,参数的新值会传给下一个方法。

匿名方法

使用情景:我们经常使用类或结构中的静态方法或实例方法来初始化委托,这种情况方法本身可以被代码的其他部分显示调用,但如果这个方法只会在初始化委托时被使用一次,其他地方不会调用到,则方法定义在类或结构中会显得占用空间,这种情况就可以使用匿名方法,匿名方法是在初始化委托时内联声明的方法,匿名方法提供了一种传递代码块作为委托参数的技术,它没有名字只有主体方法,在匿名方法不会显式声明返回类型,然而实现代码本身的行为必须通过返回一个在类型上与委托的返回类型相同的值来匹配委托的返回类型,如果委托有void类型的返回值,匿名方法就不能有返回值。

(1)除了params数组参数,匿名方法的参数列表必须在参数数量参数类型以及位置修饰符这3方面与委托匹配。

(2)当委托的参数列表不包含任何out参数匿名方法不使用任何参数的时候,我们可以通过使圆括号为空或省略圆括号来简化匿名方法的参数列表。

(3)如果委托声明的参数列表包含了params参数,那么匿名方法的参数列表将忽略params关键字

匿名方法的参数和方法内部变量的作用域

匿名方法的参数,以及声明在匿名方法内部的局部变量的作用域限制在实现方法的主体之内,如果超出这个主体范围,则不能使用。

外部变量与捕获

与委托的具名方法(也就是使用类或结构中的定义方法来初始化委托)不同,匿名方法可以访问它们外围作用域的局部变量和环境外部变量:外围作用域的变量,在匿名方法的实现代码中使用外部变量,该变量称之为被方法捕获到的。

捕获变量的生命周期的扩展

只要捕获方法(也就是这个匿名方法)还是委托的一部分,即使定义的外部变量已经离开了作用域,但它已经被匿名方法所捕获到了,所以在该方法内这个变量会一直有效

Lambda表达式

C#3.0引入了Lambda表达式,它简化了匿名方法的语法,从而避免包含这些多余的信息,我们可以通过如下步骤把匿名方法转换为Lambda表达式:
(1)删除delegate关键字
(2)在参数列表和匿名方法主体之间放Lambda运算符=>,此运算符读作goes to

进一步简化:
(1)编译器可以从委托的声明中知道委托参数的类型,因此Lambda表达式允许我们省略参数的类型(除了ref、out),如le2的赋值代码(带有类型的参数列表称为显式类型,省略类型的参数列表称为隐式类型)
(2)如果只有一个隐式类型参数,可以省略周围的( ),如le3的赋值代码
(3)Lambda表达式允许表达式的主体是语句块或表达式,如果语句块只包含了一个语句,可以省略{ },如果这语句是一个返回语句可以将语句块替换为return关键字后的表达式,如le4赋值代码。

Lambda表达式注意事项

(1)Lambda表达式参数列表中的参数必须在参数数量、类型和位置上与委托相匹配
(2)表达式的参数列表中的参数不一定需要包含类型,除非委托有ref或out参数,此时必须注明类型
(3)如果只有一个参数,并且是隐式类型的,周围的( )可以被省略,否则必须有括号
(4)如果没有参数,必须使用一组空的( )

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值