委托

思考并回答以下问题:
1.你有没有见过委托或匿名方法,具体是什么?

什么是委托?

可以认为委托是持有一个或多个方法的对象。但是委托可以执行,这时候委托会执行它“持有”的方法。

声明委托类型

委托也是一种类型,就好像类是类型一样。委托类型必须在被用来创建变量以及类型的对象之前声明。委托类型的声明语法如下:

委托类型的声明看上去与方法的声明很类似,有返回类型参数列表。返回类型和参数列表执行了委托可以接受的方法的形式。

上面图中声明制定了MyDel类型的委托只会接受不返回值并且有单个int参数的方法。

委托类型声明在3个方面与方法声明不同。委托类型声明:

  • 以delegate关键字开头
  • 没有方法主体
  • 不需要在类内部声明,因为它是类型声明

创建委托对象

委托是引用类型,因此有引用和对象。在委托类型声明后,可以声明变量并创建类型的而对象。使用委托类型声明变量的语法如下:

给委托赋值

如下的代码可以给委托赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;

public class Test : MonoBehaviour
{
public delegate void MyDel(int x);

MyDel _delVar;

void Start ()
{
_delVar = PrintNumber; // 赋值
_delVar(10); // 调用
}

void PrintNumber(int x)
{
Debug.Log(x);
}
}

给委托赋值时,可以直接将方法名赋值给委托类型的变量,但是要保证方法的参数列表和返回值和委托类型一致。

委托除了能接受一个方法,还能接收多个方法。

可以使用+=运算符给委托变量增加方法。
可以使用-=运算符从委托中移除方法。

1
2
3
_delVar += PrintNumber1;
_delVar += PrintNumber2;
_delVar -= PrintNumber1;

调用委托

调用委托和调用方法一样简单,比如上面的代码中,调用委托只需要将委托变量作为方法名,直接调用即可。

如果一个委托中有多个方法,则每个方法都会被调用。

调用带返回值的委托

如果委托有返回值并且在调用列表中有一个以上的方法,会发生下面的情况:

  • 调用列表中最后一个方法返回的值就是委托调用返回的值
  • 调用列表中所有其它方法的返回值都会被忽略

匿名方法

上面我们看过了使用实例方法来初始化委托。对于这种情况,方法本身也可以被其他代码显式调用。

但是如果方法只会被使用一次,除了创建委托语法的需要,没有必要创建独立的具名方法(包含方法名的方法)。匿名方法可以让我们创建没有方法名的方法。

如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
public delegate void MyDel(int x);

MyDel _delVar;

void Start ()
{
_delVar += delegate(int x)
{
Debug.Log(x);
};
_delVar(10);
}

这个代码和之前给委托赋值小节的代码效果相同。

使用匿名方法

我们可以在如下地方使用匿名方法:

  • 声明委托变量时作为初始化表达式。
  • 组合委托时再赋值语句的右边。
  • 给方法传委托类型的参数时。

匿名方法的语法

匿名方法表达式的语法包含如下组成部分:

  • delegate关键字
  • 参数列表,如果没有参数可以省略
  • 语句块,包含匿名方法的具体代码

返回类型

匿名方法不需要显式声明返回类型。但是在方法体内必须返回一个与委托的返回类型相同的返回值。如果委托有void类型的返回值,匿名方法就不能返回值。

例如在下面的代码中,委托的返回类型是int。匿名方法的实现代码也必须在代码路径中返回int。

1
2
3
4
5
6
7
8
9
public delegate int MyDel(int x);

MyDel _delVar;

void Start ()
{
_delVar += delegate(int x) { return x * 2; };
_delVar(10);
}

参数

匿名方法的参数列表必须在以下方面与委托匹配:

  • 参数数量
  • 参数类型及位置
  • 修饰符

有些时候可以通过使圆括号为空或省略圆括号来简化匿名方法的参数列表,但必须满足以下两个条件:

  • 委托的参数列表不包含任何out参数
  • 匿名方法不使用任何参数

Lambda表达式

上面我们学到了匿名方法,已经简化了不少的代码量,但是在C# 3.0中引入的Lambda表达式功能,进一步简化了语法。Lambda表达式可以完全替代匿名方法这一特性。

比如在匿名方法中,delegate这一关键字其实有些多余,因为编译器已经知道我们在将方法赋值给委托。

我们可以很容易地通过如下步骤把匿名方法转换为Lambda表达式:

  • 删除delegate关键字
  • 在参数列表和匿名方法主体之间放上Lambda运算符=&gt ;,这个运算符读作goes to。

上图展示了lambda表达式如何一步步简化匿名方法,最后一种只有原匿名方法1/4的代码量,更简洁,更容易理解。

上图中简化的过程如下:

  • 编译器还可以从委托的声明中知道委托参数的类型,因此Lambda表达式允许我们省略类型参数,如le2的赋值代码。
  • 带有类型的参数列表称为显式类型
  • 省略类型的参数列表称为隐式类型
  • 如果只有一个隐式类型参数,可以省略参数列表的圆括号,如le3的赋值代码所示。
  • Lambda表达式允许表达式的主体是语句块或表达式。如果语句块仅包含一个返回语句,我们可以将语句块替换为return关键字后的表达式,如le4的赋值代码所示。

有关Lambda表达式的参数列表的要点如下:

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

总结

  • 委托是持有一个或多个方法的对象。但是委托可以执行,这时候委托会执行它“持有”的方法。
  • Lambda表达式可以完全替代匿名方法,所以建议使用Lambda表达式这一更简洁的语法。
0%