183 lines
3.9 KiB
Markdown
183 lines
3.9 KiB
Markdown
# 第十课:委托和事件
|
||
|
||
## 委托(Delegate)是什么
|
||
|
||
委托 = 函数的"容器",可以把函数当作变量传递。
|
||
|
||
```csharp
|
||
// 声明委托类型
|
||
delegate int Calculator(int a, int b);
|
||
|
||
// 定义符合委托的方法
|
||
int Add(int a, int b) => a + b;
|
||
int Multiply(int a, int b) => a * b;
|
||
|
||
// 使用委托
|
||
Calculator calc = Add;
|
||
int result = calc(1, 2); // result = 3
|
||
|
||
calc = Multiply;
|
||
result = calc(3, 4); // result = 12
|
||
```
|
||
|
||
## 为什么需要委托
|
||
|
||
```csharp
|
||
// 场景:数组遍历时对每个元素做操作
|
||
void ProcessNumbers(int[] numbers, ??? operation)
|
||
{
|
||
foreach (int n in numbers)
|
||
{
|
||
??? result = operation(n);
|
||
Console.WriteLine(result);
|
||
}
|
||
}
|
||
|
||
// 没有委托,你需要写多个重载
|
||
// 有委托,可以传任意操作
|
||
ProcessNumbers(nums, x => x * 2); // 翻倍
|
||
ProcessNumbers(nums, x => x + 1); // +1
|
||
ProcessNumbers(nums, x => x * x); // 平方
|
||
```
|
||
|
||
## Action 和 Func(简化委托)
|
||
|
||
```csharp
|
||
// Action: 返回 void 的委托
|
||
Action<string> print = (msg) => Console.WriteLine(msg);
|
||
print("Hello"); // 无返回值
|
||
|
||
// Func: 有返回值的委托
|
||
Func<int, int, int> add = (a, b) => a + b;
|
||
int result = add(1, 2); // 3
|
||
|
||
// 多参数
|
||
Func<string, string, string> concat = (a, b) => a + b;
|
||
string s = concat("Hello", " World"); // "Hello World"
|
||
```
|
||
|
||
## 事件(Event)是什么
|
||
|
||
事件 = 委托的升级版,只能 += 添加监听,不能外部直接调用。
|
||
|
||
```csharp
|
||
class Button
|
||
{
|
||
// 事件声明
|
||
public event Action Clicked;
|
||
|
||
public void OnClick()
|
||
{
|
||
Console.WriteLine("按钮被点击");
|
||
// 触发事件(只能在类内部)
|
||
Clicked?.Invoke();
|
||
}
|
||
}
|
||
```
|
||
|
||
```csharp
|
||
// 使用事件
|
||
Button btn = new Button();
|
||
|
||
// 添加监听者
|
||
btn.Clicked += () => Console.WriteLine("Handler 1 执行");
|
||
btn.Clicked += () => Console.WriteLine("Handler 2 执行");
|
||
|
||
btn.OnClick();
|
||
// 输出:
|
||
// 按钮被点击
|
||
// Handler 1 执行
|
||
// Handler 2 执行
|
||
```
|
||
|
||
## 事件 + 委托的应用场景
|
||
|
||
```csharp
|
||
class Microphone
|
||
{
|
||
// 事件声明
|
||
public event Action<string> OnTranscriptReady; // 识别出文字时触发
|
||
public event Action OnListeningStarted;
|
||
public event Action OnListeningStopped;
|
||
|
||
private bool isListening;
|
||
|
||
public void StartListening()
|
||
{
|
||
isListening = true;
|
||
Console.WriteLine("麦克风开始监听...");
|
||
OnListeningStarted?.Invoke();
|
||
}
|
||
|
||
public void StopListening()
|
||
{
|
||
isListening = false;
|
||
Console.WriteLine("麦克风停止监听...");
|
||
OnListeningStopped?.Invoke();
|
||
}
|
||
|
||
// 模拟识别到文字
|
||
public void TranscriptDetected(string text)
|
||
{
|
||
Console.WriteLine($"识别到: {text}");
|
||
OnTranscriptReady?.Invoke(text); // 触发事件,通知所有监听者
|
||
}
|
||
}
|
||
```
|
||
|
||
```csharp
|
||
// 使用
|
||
Microphone mic = new Microphone();
|
||
|
||
// 订阅事件
|
||
mic.OnTranscriptReady += (text) =>
|
||
{
|
||
Console.WriteLine($"收到文字: {text}");
|
||
};
|
||
|
||
mic.OnListeningStarted += () =>
|
||
{
|
||
Console.WriteLine("开始记录...");
|
||
};
|
||
|
||
mic.OnListeningStopped += () =>
|
||
{
|
||
Console.WriteLine("停止记录");
|
||
};
|
||
|
||
// 触发
|
||
mic.StartListening();
|
||
mic.TranscriptDetected("今天天气不错");
|
||
mic.TranscriptDetected("大家一起加油");
|
||
mic.StopListening();
|
||
```
|
||
|
||
## 事件 vs 委托
|
||
|
||
| | 委托 | 事件 |
|
||
|---|---|---|
|
||
| 外部添加监听 | ✓ | ✓ |
|
||
| 外部调用 | ✓ | ✗ |
|
||
| 主要用途 | 传递函数 | 通知机制 |
|
||
|
||
## 移除监听
|
||
|
||
```csharp
|
||
Action handler = () => Console.WriteLine("Handler");
|
||
|
||
btn.Clicked += handler;
|
||
btn.Clicked -= handler; // 移除
|
||
```
|
||
|
||
## 实际项目结构
|
||
|
||
```
|
||
语音识别服务
|
||
├── OnTranscriptReady(text) → UI 更新文字
|
||
├── OnListeningStarted() → UI 显示监听状态
|
||
├── OnListeningStopped() → UI 停止动画
|
||
└── OnError(error) → UI 显示错误
|
||
```
|
||
|
||
事件让各部分解耦,不需要互相引用。
|