# 委托 概念: 委托是==可以引用方法的类型== 可以理解为“函数的容器”或“存方法的变量” 作用: 1. 把方法赋值给变量 2. 把方法作为参数传递 3. 让一个地方可以调用多个方法 4. 为事件、匿名函数、lambda 提供基础 简单理解: 普通变量存数据 委托变量存“符合条件的方法” ---- # 通常用法 1. 作为类的成员,保存回调方法 2. 作为方法参数,让调用者决定具体执行什么逻辑 3. 同时绑定多个方法,实现多播调用 4. 配合 `Action`、`Func`、匿名函数和 lambda 使用 实际开发中: 自定义委托仍然会用到 但很多简单场景更常使用系统自带的 `Action` 和 `Func` ---- # 语法 关键字: ```csharp delegate ``` 定义委托: ```csharp 访问修饰符 delegate 返回值类型 委托名(参数列表); ``` 示例: ```csharp public delegate void MyDelegate(string message); ``` 说明: `MyDelegate` 只能保存: 参数是 `string` 返回值是 `void` 的方法 ---- # 基础案例 ```csharp using System; public delegate void MyDelegate(string message); class Program { static void PrintMessage(string message) { Console.WriteLine(message); } static void Main(string[] args) { MyDelegate del = PrintMessage; del("你好,委托"); } } ``` 输出: ```text 你好,委托 ``` 理解: 1. `MyDelegate` 规定了方法的格式 2. `PrintMessage` 的参数和返回值符合这个格式 3. 所以可以赋值给 `del` 4. 调用 `del(...)`,本质上就是调用 `PrintMessage(...)` ---- # 方法必须匹配委托格式 ```csharp public delegate int Calculate(int a, int b); static int Add(int a, int b) { return a + b; } Calculate calculate = Add; Console.WriteLine(calculate(10, 20)); ``` 正确匹配: 参数数量一致 参数类型一致 返回值类型一致 错误示例: ```csharp static void Print(int a, int b) { } // Calculate calculate = Print; ``` 原因: `Calculate` 要求返回 `int` `Print` 的返回值是 `void` 两者不匹配 ---- # 委托的赋值和调用 赋值: ```csharp MyDelegate del = PrintMessage; ``` 调用: ```csharp del("hello"); ``` 也可以使用 `Invoke`: ```csharp del.Invoke("hello"); ``` 通常写法: 直接写 `del(...)` 更常见 `Invoke(...)` 的语义更明确 ---- # 委托可以作为参数 作用: 把“具体要做什么”交给调用者决定 ```csharp using System; public delegate void HandleNumber(int number); class Program { static void Process(int number, HandleNumber handler) { handler(number); } static void PrintNumber(int number) { Console.WriteLine("数字:" + number); } static void PrintDouble(int number) { Console.WriteLine("两倍:" + number * 2); } static void Main(string[] args) { Process(10, PrintNumber); Process(10, PrintDouble); } } ``` 输出: ```text 数字:10 两倍:20 ``` 理解: `Process` 不关心具体处理方式 只要求传进来的方法符合 `HandleNumber` 的格式 ---- # 多播委托 一个委托变量可以同时绑定多个方法: ```csharp using System; public delegate void MyDelegate(); class Program { static void Method1() { Console.WriteLine("执行方法1"); } static void Method2() { Console.WriteLine("执行方法2"); } static void Main(string[] args) { MyDelegate del = Method1; del += Method2; del(); } } ``` 输出: ```text 执行方法1 执行方法2 ``` 取消绑定: ```csharp del -= Method2; ``` 说明: 1. `+=` 用来添加方法 2. `-=` 用来移除方法 3. 调用时会按添加顺序依次执行 4. 多播委托最适合返回值为 `void` 的情况 ---- # 有返回值的多播委托 ```csharp public delegate int Calculate(); static int Method1() { return 1; } static int Method2() { return 2; } Calculate calculate = Method1; calculate += Method2; int result = calculate(); Console.WriteLine(result); ``` 输出: ```text 2 ``` 说明: 两个方法都会执行 但委托调用的最终返回值,只会保留最后一个方法的返回值 所以: 多播委托通常更适合 `void` 返回值 ---- # 空委托要先判断 错误示例: ```csharp MyDelegate del = null; // del(); ``` 原因: 委托变量是引用类型 没有绑定方法时可能为 `null` 常见写法: ```csharp if (del != null) { del(); } ``` 更简洁的写法: ```csharp del?.Invoke(); ``` 说明: `?.` 会先判断 `del` 是否为 `null` 不为 `null` 才调用 `Invoke()` ---- # Action `Action` 是系统自带的委托类型: ==无返回值== 无参: ```csharp Action action = PrintHello; action(); static void PrintHello() { Console.WriteLine("Hello"); } ``` 有参数: ```csharp Action print = PrintMessage; print("你好"); static void PrintMessage(string message) { Console.WriteLine(message); } ``` 说明: `Action` 后面的泛型参数表示方法参数类型 它的返回值始终是 `void` ---- # Func `Func` 是系统自带的泛型委托类型: ==有返回值== ```csharp Func add = Add; int result = add(10, 20); Console.WriteLine(result); static int Add(int a, int b) { return a + b; } ``` 说明: ```csharp Func<参数1类型, 参数2类型, 返回值类型> ``` 上面的: ```csharp Func ``` 表示: 前两个 `int` 是参数类型 最后一个 `int` 是返回值类型 ---- # 自定义委托、Action、Func 的区别 | 对比点 | 自定义委托 | `Action` | `Func` | |---|---|---|---| | 是否需要自己声明 | 需要 | 不需要 | 不需要 | | 返回值 | 自己定义 | 必须是 `void` | 必须有返回值 | | 参数 | 自己定义 | 可有多个 | 可有多个 | | 常见用途 | 语义明确的回调 | 简单无返回值操作 | 简单有返回值操作 | 实际使用建议: 1. 简单无返回值回调,优先考虑 `Action` 2. 简单有返回值回调,优先考虑 `Func` 3. 需要清晰业务语义时,可以定义自己的委托类型 ---- # 和事件的关系 委托: 可以在类外部赋值 也可以在类外部直接调用 事件: 是对委托的进一步封装 类外部只能订阅和取消订阅 不能直接调用事件 可以和[[事件]]一起理解: 委托负责“存方法和调用方法” 事件负责“更安全地对外发布通知” ---- # 注意 1. 委托是一种引用类型 2. 委托变量只能保存签名匹配的方法 3. 签名主要看参数和返回值 4. 委托可以绑定一个或多个方法 5. `+=` 添加方法,`-=` 移除方法 6. 调用可能为 `null` 的委托前要先判断 7. `Action` 无返回值,`Func` 有返回值 8. 自定义委托不能只靠返回值不同来重名 ---- # 易错点 1. 委托不是方法本身,而是“引用方法的变量” 2. 委托类型和方法签名不匹配时,不能赋值 3. `Add` 和 `+=` 不一样,委托绑定方法用 `+=` 4. 多播委托有返回值时,只能拿到最后一个方法的返回值 5. `del?.Invoke()` 可以避免空引用异常 ---- # 一句话记忆 ==委托就是一种可以存方法、传方法、调方法的类型。== ---- # 面试/复习时怎么说 可以这样答: “委托是一种引用方法的类型,可以把符合指定签名的方法赋值给委托变量,再通过委托统一调用。 它可以作为参数传递,也可以绑定多个方法,常用于回调和事件机制。 实际开发中,简单场景通常使用系统自带的 `Action` 和 `Func`。” ---- 引用: 1. Microsoft Learn: [Delegates](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/) 2. Microsoft Learn: [Action Delegate](https://learn.microsoft.com/en-us/dotnet/api/system.action) 3. Microsoft Learn: [Func Delegate](https://learn.microsoft.com/en-us/dotnet/api/system.func-2) #委托 #delegate #Action #Func #进阶