文章目录
- 14.1 什么是委托
- 14.2 委托概述
- 14.3 声明委托类型
- 14.4 创建委托对象
- 14.5 给委托赋值
- 14.6 组合委托
- 14.7 为委托添加方法
- 14.8 从委托移除方法
- 14.9 调用委托
- 14.10 委托的示例(*)
- 14.11 调用带返回值的委托
- 14.12 调用带引用参数的委托
- 14.13 匿名方法
- 14.13.1 使用匿名方法
- 14.13.2 匿名方法的语法
- 14.13.3 变量和参数的作用域
- 14.14 Lambda 表达式
14.1 什么是委托
委托会执行它所“持有”的方法,可以看成是类型安全、面向对象的 C++ 函数指针。
14.2 委托概述
委托是一种用户定义类型,其与类的区别可简单概括如下:
- 类是数据和方法的集合。
- 委托持有一个或多个方法。
委托类型的声明和方法类似,只是没有实现块。
可以将 delegate 看作一个包含有序方法列表的对象,这些方法具有相同的签名和返回类型。
- 方法的列表称为调用列表。
- 方法可以来自任何类或结构,只要满足如下的匹配:
- 委托的返回类型。
- 委托的签名(包含 ref 和 out)。
- 方法可以是实例方法或者静态方法。
- 调用委托时,会执行调用列表中的所有方法。
14.3 声明委托类型
- 以关键字 delegate 开头。
- 没有方法主体。
- 不需要在类内部声明,因为委托是类型声明。
14.4 创建委托对象
声明委托类型的变量:
创建委托对象的两种方式:
- 使用 new 运算符。
-
使用快捷语法。
方法和委托类型之间存在隐式转换,该快捷语法利用了这一特性。
14.5 给委托赋值
可以通过赋值改变包含在委托变量中的引用,旧的委托对象会被垃圾回收器回收。
14.6 组合委托
使用 +
组合委托,委托对象被创建后不能再被改变。
14.7 为委托添加方法
在使用 +=
运算符时,实际发生的是创建了一个新的委托,其调用列表是左边的委托加上右边方法的组合,然后将这个新的委托赋值给 delVar
。
14.8 从委托移除方法
在使用 -=
运算符时,实际上也是创建了一个新的委托。
- 如果在调用列表中的方法有多个实例,则删除最后一个。
- 可以删除委托中不存在的方法,结果没有影响。
14.9 调用委托
- 可以使用两种方法调用委托。
- 像方法一样调用。
- 使用 InVoke 调用。
- 如果一个方法在调用列表中多次出现,则调用委托时会被多次执行。
- 试图调用空委托将会抛出异常,需要判断委托是否为 null。
14.10 委托的示例(*)
14.11 调用带返回值的委托
- 调用列表中最后一个方法的返回值就是调用委托的返回值。
- 调用列表中之前方法的返回值都会被忽略。
14.12 调用带引用参数的委托
如果委托中有引用参数,参数值可能会根据调用列表中的方法而改变。
在调用委托列表中的下一个方法,参数的新值会被传给下一个方法。
14.13 匿名方法
匿名方法是在实例化委托时内联声明的方法。
14.13.1 使用匿名方法
可以在以下情况中使用匿名方法:
- 声明委托变量时,充当初始化表达式。
- 组合委托时,充当赋值表达式。
- 为委托增加事件时,充当事件表达式。
14.13.2 匿名方法的语法
-
返回类型
匿名方法不会显示声明返回值,但必须返回与委托返回值相同类型的值。
-
参数
匿名方法可以省略圆括号进行简化,但需要满足如下两个条件:
- 委托的参数列表不包含任何 out 参数。
- 匿名方法不使用任何参数。
-
params 参数
如果委托声明的参数列表中包含 params,那么匿名方法的参数列表不能使用 params 关键字。
- 委托类型声明指定最后一个参数为 params 类型。
- 匿名方法的参数列表必须不带 params。
14.13.3 变量和参数的作用域
参数及声明在匿名方法内部的局部变量的作用域限制在实现代码的主体之内。
-
外部变量
匿名方法可以访问它们外围作用域的局部变量和环境。
- 外围作用域的变量称为 外部变量。
- 用匿名方法实现代码中的外部变量称为 被方法捕获。
-
捕获变量生命周期的扩展
只要捕获方法是委托的一部分,即使变量已经离开了作用域,捕获的外部变量也会一直有效。
14.14 Lambda 表达式
C# 2.0 引入了匿名方法,C# 3.0 引入了 Lambda 表达式。可以使用 Lambda 表达式代替匿名方法:
- 删除 delegate 关键字。
- 使用 Lambda 运算符
=>
。
- 编译器可以从委托声明中直到委托参数的类型,因此 Lambda 表达式可以省略类型参数。
- 显示类型:带有类型的参数列表。
- 隐式类型:省略类型的参数列表。
- 如果只有一个隐式类型参数,可以省略圆括号。
- 如果语句块只包含一个返回语句,可以省略
return
。
- 若委托有 ref 或 out 参数,则 Lambda 表达式必须为显示类型。
- 如果没有参数,必须使用一组空的圆括号。