====== 第八章 委托与事件 ======
===== 8.1 委托基础 =====
=== 什么是委托 ===
委托(Delegate)是一种类型,它安全地封装了方法的引用。可以把委托看作是指向方法的指针,但它比C/C++的函数指针更安全、更灵活。
=== 定义和使用委托 ===
// 1. 定义委托类型
public delegate void MyDelegate(string message);
public delegate int CalculatorDelegate(int a, int b);
// 2. 创建与委托匹配的方法
public static void ShowMessage(string msg)
{
Console.WriteLine($"消息: {msg}");
}
public static void ShowUpperMessage(string msg)
{
Console.WriteLine($"消息: {msg.ToUpper()}");
}
public static int Add(int a, int b) => a + b;
public static int Multiply(int a, int b) => a * b;
// 3. 使用委托
public class DelegateDemo
{
public static void Main()
{
// 创建委托实例
MyDelegate del = ShowMessage;
del("Hello World"); // 调用
// 委托多播(组合多个方法)
MyDelegate multiDel = ShowMessage;
multiDel += ShowUpperMessage;
multiDel("Hello"); // 两个方法都会执行
// 使用CalculatorDelegate
CalculatorDelegate calc = Add;
Console.WriteLine(calc(5, 3)); // 8
calc = Multiply;
Console.WriteLine(calc(5, 3)); // 15
}
}
===== 8.2 内置委托类型 =====
=== Action委托 ===
Action是无返回值的委托,支持0-16个参数。
// Action委托
Action action1 = () => Console.WriteLine("无参数");
Action action2 = msg => Console.WriteLine(msg);
Action action3 = (a, b) => Console.WriteLine(a + b);
action1();
action2("Hello");
action3(5, 3);
=== Func委托 ===
Func是有返回值的委托,最后一个类型参数是返回值类型。
// Func委托
Func func1 = () => 42;
Func func2 = (a, b) => a + b;
Func func3 = str => str.Length > 5;
Console.WriteLine(func1()); // 42
Console.WriteLine(func2(5, 3)); // 8
Console.WriteLine(func3("Hello World")); // True
=== Predicate委托 ===
Predicate是返回bool的特定委托。
// Predicate委托
Predicate isEven = n => n % 2 == 0;
Predicate isLong = str => str.Length > 10;
Console.WriteLine(isEven(4)); // True
Console.WriteLine(isLong("Hi")); // False
===== 8.3 匿名方法与Lambda表达式 =====
=== 匿名方法 ===
// 传统委托
MyDelegate del1 = ShowMessage;
// 匿名方法(C# 2.0)
MyDelegate del2 = delegate(string msg)
{
Console.WriteLine($"匿名方法: {msg}");
};
// 使用
Action printSquare = delegate(int n)
{
Console.WriteLine(n * n);
};
printSquare(5); // 25
=== Lambda表达式 ===
// 表达式Lambda
Func square = x => x * x;
Func add = (a, b) => a + b;
// 语句Lambda
Action greet = name =>
{
Console.WriteLine($"Hello, {name}!");
Console.WriteLine("Welcome!");
};
// 多参数
Func sum = (a, b, c) => a + b + c;
// 无参数
Action sayHello = () => Console.WriteLine("Hello!");
// 使用
Console.WriteLine(square(5)); // 25
Console.WriteLine(add(3, 4)); // 7
greet("Alice");
===== 8.4 委托的高级特性 =====
=== 委托多播 ===
public class MulticastDemo
{
public static void Method1() => Console.WriteLine("方法1");
public static void Method2() => Console.WriteLine("方法2");
public static void Method3() => Console.WriteLine("方法3");
public static void Main()
{
Action multi = Method1;
multi += Method2;
multi += Method3;
Console.WriteLine("=== 执行所有方法 ===");
multi();
Console.WriteLine("\n=== 移除方法2 ===");
multi -= Method2;
multi();
// 获取调用列表
foreach (Delegate d in multi.GetInvocationList())
{
Console.WriteLine($"方法: {d.Method.Name}");
}
}
}
=== 泛型委托 ===
// 自定义泛型委托
public delegate TResult MyFunc(T arg);
public class GenericDelegateDemo
{
public static void Main()
{
// 使用自定义泛型委托
MyFunc intToString = n => $"数字: {n}";
Console.WriteLine(intToString(42));
// 使用内置泛型委托
Func stringLength = s => s.Length;
Console.WriteLine(stringLength("Hello"));
}
}
===== 8.5 事件 =====
=== 事件基础 ===
事件是基于委托的一种特殊机制,用于实现发布-订阅模式。
// 定义事件参数
public class OrderEventArgs : EventArgs
{
public string OrderId { get; set; }
public decimal Amount { get; set; }
public DateTime OrderTime { get; set; }
}
// 发布者
public class OrderProcessor
{
// 声明事件
public event EventHandler OrderPlaced;
public event EventHandler OrderCompleted;
public void PlaceOrder(string orderId, decimal amount)
{
Console.WriteLine($"处理订单: {orderId}");
// 触发事件
OrderPlaced?.Invoke(this, new OrderEventArgs
{
OrderId = orderId,
Amount = amount,
OrderTime = DateTime.Now
});
// 处理订单...
Thread.Sleep(1000);
// 触发完成事件
OrderCompleted?.Invoke(this, EventArgs.Empty);
}
}
// 订阅者
public class EmailNotifier
{
public void Subscribe(OrderProcessor processor)
{
processor.OrderPlaced += OnOrderPlaced;
}
private void OnOrderPlaced(object sender, OrderEventArgs e)
{
Console.WriteLine($"[邮件通知] 订单 {e.OrderId} 已下单,金额: {e.Amount:C}");
}
}
public class SmsNotifier
{
public void Subscribe(OrderProcessor processor)
{
processor.OrderPlaced += (sender, e) =>
{
Console.WriteLine($"[短信通知] 订单 {e.OrderId} 通知已发送");
};
}
}
// 使用
var processor = new OrderProcessor();
var emailNotifier = new EmailNotifier();
var smsNotifier = new SmsNotifier();
emailNotifier.Subscribe(processor);
smsNotifier.Subscribe(processor);
processor.PlaceOrder("ORD-001", 199.99m);
===== 8.6 标准事件模式 =====
=== EventHandler模式 ===
public class Stock
{
public string Symbol { get; }
private decimal price;
public Stock(string symbol, decimal price)
{
Symbol = symbol;
this.price = price;
}
public decimal Price
{
get => price;
set
{
if (price == value) return;
decimal oldPrice = price;
price = value;
// 触发价格变化事件
OnPriceChanged(new PriceChangedEventArgs(oldPrice, price));
}
}
// 事件声明
public event EventHandler PriceChanged;
// 触发事件的受保护虚方法
protected virtual void OnPriceChanged(PriceChangedEventArgs e)
{
PriceChanged?.Invoke(this, e);
}
}
// 事件参数
public class PriceChangedEventArgs : EventArgs
{
public decimal OldPrice { get; }
public decimal NewPrice { get; }
public decimal ChangePercent => (NewPrice - OldPrice) / OldPrice * 100;
public PriceChangedEventArgs(decimal oldPrice, decimal newPrice)
{
OldPrice = oldPrice;
NewPrice = newPrice;
}
}
// 使用
var stock = new Stock("MSFT", 150.00m);
stock.PriceChanged += (sender, e) =>
{
Console.WriteLine($"股票价格变化: {e.OldPrice:C} -> {e.NewPrice:C} ({e.ChangePercent:F2}%)");
};
stock.Price = 155.00m;
stock.Price = 160.00m;
===== 8.7 实战:事件驱动架构 =====
// 温度监控示例
public class TemperatureSensor
{
private Random random = new Random();
private double currentTemperature;
public double CurrentTemperature => currentTemperature;
public event EventHandler TemperatureChanged;
public event EventHandler TemperatureTooHigh;
public event EventHandler TemperatureTooLow;
public void SimulateReading()
{
// 模拟温度读数(20-40度之间随机)
double newTemp = 20 + random.NextDouble() * 20;
UpdateTemperature(newTemp);
}
public void SetTemperature(double temperature)
{
UpdateTemperature(temperature);
}
private void UpdateTemperature(double newTemp)
{
double oldTemp = currentTemperature;
currentTemperature = newTemp;
var args = new TemperatureEventArgs(oldTemp, newTemp);
// 触发温度变化事件
TemperatureChanged?.Invoke(this, args);
// 检查阈值
if (newTemp > 35)
{
TemperatureTooHigh?.Invoke(this, args);
}
else if (newTemp < 22)
{
TemperatureTooLow?.Invoke(this, args);
}
}
}
public class TemperatureEventArgs : EventArgs
{
public double OldTemperature { get; }
public double NewTemperature { get; }
public double Change => NewTemperature - OldTemperature;
public DateTime Timestamp { get; }
public TemperatureEventArgs(double oldTemp, double newTemp)
{
OldTemperature = oldTemp;
NewTemperature = newTemp;
Timestamp = DateTime.Now;
}
}
// 不同的监听器
public class TemperatureLogger
{
private List logs = new List();
public void Subscribe(TemperatureSensor sensor)
{
sensor.TemperatureChanged += OnTemperatureChanged;
}
private void OnTemperatureChanged(object sender, TemperatureEventArgs e)
{
string log = $"[{e.Timestamp:HH:mm:ss}] 温度: {e.OldTemperature:F1}°C -> {e.NewTemperature:F1}°C";
logs.Add(log);
Console.WriteLine($"[日志] {log}");
}
public void SaveToFile(string filename)
{
File.WriteAllLines(filename, logs);
}
}
public class TemperatureAlert
{
public void Subscribe(TemperatureSensor sensor)
{
sensor.TemperatureTooHigh += (s, e) =>
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"⚠️ 高温警告: {e.NewTemperature:F1}°C 超过安全阈值!");
Console.ResetColor();
};
sensor.TemperatureTooLow += (s, e) =>
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"❄️ 低温警告: {e.NewTemperature:F1}°C 低于设定阈值!");
Console.ResetColor();
};
}
}
public class TemperatureDisplay
{
public void Subscribe(TemperatureSensor sensor)
{
sensor.TemperatureChanged += (s, e) =>
{
Console.WriteLine($"📊 当前温度: {e.NewTemperature:F1}°C");
};
}
}
// 使用
public class TemperatureDemo
{
public static void Main()
{
var sensor = new TemperatureSensor();
var logger = new TemperatureLogger();
var alert = new TemperatureAlert();
var display = new TemperatureDisplay();
logger.Subscribe(sensor);
alert.Subscribe(sensor);
display.Subscribe(sensor);
Console.WriteLine("=== 模拟温度监控 ===");
for (int i = 0; i < 10; i++)
{
sensor.SimulateReading();
Thread.Sleep(500);
}
// 触发高温
Console.WriteLine("\n=== 测试高温警报 ===");
sensor.SetTemperature(38);
// 触发低温
Console.WriteLine("\n=== 测试低温警报 ===");
sensor.SetTemperature(18);
}
}
===== 8.8 事件最佳实践 =====
=== 线程安全的事件触发 ===
public class ThreadSafePublisher
{
private EventHandler myEvent;
private readonly object eventLock = new object();
public event EventHandler MyEvent
{
add
{
lock (eventLock)
{
myEvent += value;
}
}
remove
{
lock (eventLock)
{
myEvent -= value;
}
}
}
protected virtual void OnMyEvent(MyEventArgs e)
{
EventHandler handler;
lock (eventLock)
{
handler = myEvent;
}
handler?.Invoke(this, e);
}
}
===== 练习题 =====
=== 基础练习 ===
1. **委托基础**:创建以下委托和使用:
- 一个无返回值、带string参数的委托
- 一个返回int、带两个int参数的委托
- 分别使用命名方法和Lambda表达式实例化
2. **多播委托**:创建一个多播委托,依次执行以下操作:
- 输出当前时间
- 输出"任务开始"
- 输出"任务完成"
- 然后移除"任务开始",再次执行
3. **简单事件**:创建Button类,包含Click事件。创建多个按钮,为每个按钮添加不同的点击处理程序。
=== 进阶练习 ===
4. **文件监视器**:创建FileWatcher类:
- FileCreated事件
- FileDeleted事件
- FileChanged事件
- 模拟文件系统操作并触发相应事件
5. **进度报告系统**:创建FileDownloader类:
- DownloadProgressChanged事件(报告进度百分比)
- DownloadCompleted事件
- DownloadFailed事件
- 模拟下载过程并触发事件
6. **事件链**:创建Workflow类,实现步骤链:
- StepStarted事件
- StepCompleted事件
- StepFailed事件
- 支持步骤重试机制
=== 挑战练习 ===
7. **消息总线**:实现一个简单的消息总线:
- 支持发布/订阅模式
- 支持消息过滤
- 支持同步和异步处理
- 消息队列和持久化
8. **反应式编程**:使用委托和事件实现简单的反应式系统:
- Observable和Observer模式
- 支持操作符(Where、Select、Merge等)
- 支持冷/热Observable
===== 本章小结 =====
本章学习了C#的委托与事件:
- 委托的定义、实例化和调用
- 内置委托类型(Action、Func、Predicate)
- 匿名方法与Lambda表达式
- 委托的多播特性
- 事件的声明、订阅和触发
- 标准事件模式
- 事件驱动架构实战
委托和事件是实现松耦合、可扩展架构的重要工具。