Files
My-Notes/笔记/CSharp/进阶/委托.md
T
2026-08-09 12:41:53 +08:00

427 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 委托
概念:
委托是==可以引用方法的类型==
可以理解为“函数的容器”或“存方法的变量”
作用:
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<string> print = PrintMessage;
print("你好");
static void PrintMessage(string message)
{
Console.WriteLine(message);
}
```
说明:
`Action` 后面的泛型参数表示方法参数类型
它的返回值始终是 `void`
----
# Func
`Func` 是系统自带的泛型委托类型:
==有返回值==
```csharp
Func<int, int, int> 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, int>
```
表示:
前两个 `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
#进阶