# 第十课：委托和事件

## 委托（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 显示错误
```

事件让各部分解耦，不需要互相引用。
