可以认为委托是持有一个或多个方法的对象,但委托与对象不同,可以执行委托,这时委托会执行它所持有的方法。
如果学过C/C++可以将委托理解成函数指针的升级版。

初识委托

委托与类平级,所以一般声明在类声明的位置,委托的访问性默认是internal。

1
2
3
4
5
6
7
8
9
10
internal class Program {
static void Main(string[] args) {
MyDel del = new MyDel(SayHello);
del.Invoke();
}
static void SayHello() {
Console.WriteLine("Hello world!");
}
}
delegate int MyDel(int i);

创建委托实例时,可以传入实例方法或者静态方法的方法名。

初始化委托

我们已经见到了new的赋值方法

1
MyDel del = new MyDel(Ix10);

也可以使用委托赋值的快捷方法

1
MyDel del = SayHello;

组合委托

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

1
2
3
MyDel del1 = SayHello;
MyDel del2 = Human.Eat;
MyDel del3 = del1+del2;

多播委托

我们已经展示过了组合委托,通过+运算符可以将两个委托组合起来,+和+=运算符还可以给委托增加方法到委托的调用队列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
internal class Program {
static void Main(string[] args) {
MyDel del = SayHello;
del += Human.Eat;
del = del +SayHello;
del();
}
static void SayHello() {
Console.WriteLine("hello world");
}
}
delegate void MyDel();
public class Human {
public static void Eat() {
Console.WriteLine("我可以吃饭");
}
}

在使用+=运算符时,实际上发生的是创建了一个新的委托,其调用列表是左边的委托加上右边方法的组合,然后将这个委托赋值给del。
由代码的执行结果来看,其调用顺序与添加方法的顺序相同。

移除委托方法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
internal class Program {
static async Task Main(string[] args) {
Action action = SayHello;
action += Human.Eat;
action = action + SayHello;
action = action - SayHello;
action -= Human.Eat;
action();
}
static void SayHello() {
Console.WriteLine("hello world");
}
}
delegate void MyDel();
public class Human {
public static void Eat() {
Console.WriteLine("我可以吃饭");
}
}

显式异步调用多播委托

多播委托默认是同步调用的,但我们可以使用Thread或Task显式异步调用。
Thread:

1
2
3
4
5
//using System.Threading;
MyDel del = SayHello;
del += Human.Eat;
Thread thread = new Thread(new ThreadStart(del));//可以直接传入方法名或委托实例
thread.Start();

Task:

1
2
3
4
5
6
7
static async Task Main(string[] args) {
Action action = SayHello;
action += Human.Eat;
Task task = new Task(action);
task.Start();
await Task.Delay(1000);
}

异步调用看不懂没关系,我会在异步编程中介绍的。Task之所以加了Delay是因为如果不await,代码会在执行Human.Eat或者SayHello之前退出。

Action<>委托

Action委托无需自己定义,由C#语言定义只需要使用即可。
Action委托是泛型委托用于无返回值的函数,泛型列表中是被委托函数的参数列表。

1
2
3
4
5
6
7
8
9
internal class Program {
static void Main(string[] args) {
Action<string> action = Say;
action("你好世界");
}
static void Say(string str) {
Console.WriteLine(str);
}
}

Func<>委托

Func<>委托无需自己定义,由C#语言定义只需要使用即可。
Func<>委托是泛型委托用于有返回值的函数,泛型列表中最后一个是方法返回值类型,前几个是被委托函数的参数类型。

1
2
3
4
5
6
7
8
9
10
internal class Program {
static void Main(string[] args) {
Func<int, int, int> func = new Func<int, int, int>(add);
int res = func(3,4);
Console.WriteLine(res);
}
static int add(int a,int b) {
return a + b;
}
}