C# 委托与事件

17.1 委托概念

委托是类型安全的方法指针,允许将方法作为参数传递。

声明委托

1
2
3
4
5
6
7
8
9
10
11
12
13
// 委托声明
public delegate int MathOperation(int a, int b);

// 方法
int Add(int a, int b) => a + b;
int Subtract(int a, int b) => a - b;

// 使用委托
MathOperation op = Add;
Console.WriteLine(op(10, 5)); // 15

op = Subtract;
Console.WriteLine(op(10, 5)); // 5

17.2 Action 与 Func

Action(无返回值)

1
2
3
4
5
6
7
8
9
10
11
// Action 无参数
Action greet = () => Console.WriteLine("Hello");
greet();

// Action<T> 一个参数
Action<string> print = Console.WriteLine;
print("Hello");

// Action<T1, T2> 两个参数
Action<int, int> show = (x, y) => Console.WriteLine($"{x}, {y}");
show(10, 20);

Func(有返回值)

1
2
3
4
5
6
7
8
9
10
11
// Func<TResult> 无参数
Func<double> getPi = () => 3.14159;
Console.WriteLine(getPi());

// Func<T, TResult> 一个参数
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 25

// Func<T1, T2, TResult> 两个参数
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 4)); // 7

17.3 多播委托

1
2
3
4
5
6
7
8
9
10
11
12
Action<string> log = msg => Console.WriteLine($"Log: {msg}");
log += msg => Console.WriteLine($"File: {msg}");
log += msg => Console.WriteLine($"Email: {msg}");

log("Error occurred");
// 输出:
// Log: Error occurred
// File: Error occurred
// Email: Error occurred

// 移除
log -= msg => Console.WriteLine($"File: {msg}");

17.4 事件

定义事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Button
{
// 定义事件
public event EventHandler Click;

// 触发事件
public void OnClick()
{
Click?.Invoke(this, EventArgs.Empty);
}
}

// 使用
Button btn = new();
btn.Click += (sender, e) => Console.WriteLine("按钮被点击");
btn.OnClick();

自定义事件参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class TemperatureChangedEventArgs : EventArgs
{
public double OldTemperature { get; }
public double NewTemperature { get; }

public TemperatureChangedEventArgs(double oldTemp, double newTemp)
{
OldTemperature = oldTemp;
NewTemperature = newTemp;
}
}

public class Thermostat
{
public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;

private double _temperature;
public double Temperature
{
get => _temperature;
set
{
if (_temperature != value)
{
double old = _temperature;
_temperature = value;
TemperatureChanged?.Invoke(this,
new TemperatureChangedEventArgs(old, value));
}
}
}
}

// 使用
var thermo = new Thermostat();
thermo.TemperatureChanged += (sender, e) =>
Console.WriteLine($"温度从 {e.OldTemperature} 变为 {e.NewTemperature}");

thermo.Temperature = 25;

17.5 事件最佳实践

1. 使用 EventHandler

1
2
3
4
5
6
// 推荐
public event EventHandler<MyEventArgs> MyEvent;

// 不推荐
public delegate void MyHandler(object sender, MyEventArgs e);
public event MyHandler MyEvent;

2. 空条件运算符

1
2
3
4
5
6
// 安全触发
MyEvent?.Invoke(this, EventArgs.Empty);

// 等价于
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);

3. 事件访问器

1
2
3
4
5
6
7
private EventHandler _myEvent;

public event EventHandler MyEvent
{
add => _myEvent += value;
remove => _myEvent -= value;
}

17.6 本章小结

  • 委托是类型安全的方法指针
  • Action 无返回值,Func 有返回值
  • 多播委托可组合多个方法
  • 事件基于委托,提供发布/订阅模式
  • 使用 EventHandler<T> 定义事件
  • 使用 ?.Invoke 安全触发事件

练习题

  1. 使用 Func 实现计算器
  2. 创建 Timer 类,每秒触发事件
  3. 使用多播委托实现日志记录
  4. 以下代码输出什么?
    1
    2
    3
    Action a = () => Console.WriteLine("A");
    a += () => Console.WriteLine("B");
    a();
ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
C#基础教程 系列
第 17/20 篇
分享: