一、委托基础
1. 委托定义
委托是一种类型安全的函数指针,它允许将方法作为参数传递给其他方法。
// 声明一个委托类型
public delegate void MyDelegate(string message);
// 使用委托
public class Program
{
public static void Main()
{
// 创建委托实例并指向方法
MyDelegate del = new MyDelegate(ShowMessage);
// 调用委托
del("Hello, Delegate!");
}
public static void ShowMessage(string msg)
{
Console.WriteLine(msg);
}
}
2. 委托的多播
委托可以引用多个方法,形成多播委托。
public delegate void MultiCastDelegate(string message);
public class Program
{
public static void Main()
{
MultiCastDelegate del = Method1;
del += Method2; // 添加另一个方法
del += Method3; // 可以继续添加
del("Multicast message"); // 调用所有方法
del -= Method2; // 移除一个方法
}
public static void Method1(string msg) => Console.WriteLine($"1: {msg}");
public static void Method2(string msg) => Console.WriteLine($"2: {msg}");
public static void Method3(string msg) => Console.WriteLine($"3: {msg}");
}
3. 泛型委托
C#提供了泛型委托类型,如Func
和Action
。
// Action委托(无返回值)
Action<string> printAction = s => Console.WriteLine(s);
printAction("Hello Action!");
// Func委托(有返回值)
Func<int, int, int> add = (a, b) => a + b;
int result = add(3, 5);
Console.WriteLine(result); // 输出8
常用内置泛型委托:
Action<T>
- 表示无返回值的方法Func<T, TResult>
- 表示有返回值的方法Predicate<T>
- 表示返回bool的方法
二、事件基础
1. 事件定义
事件是基于委托的,它提供了一种更安全的方式来发布和订阅通知。
public class Button
{
// 1. 定义委托类型
// public delegate void ClickHandler(object sender, EventArgs e);
// 2. 使用EventHandler标准模式(推荐)
public event EventHandler Click;
// 触发事件的方法
public void OnClick()
{
Click?.Invoke(this, EventArgs.Empty);
}
}
public class Program
{
public static void Main()
{
var button = new Button();
// 订阅事件
button.Click += Button_Click;
button.OnClick(); // 触发事件
}
private static void Button_Click(object sender, EventArgs e)
{
Console.WriteLine("Button clicked!");
}
}
2. 自定义事件参数
public class CustomEventArgs : EventArgs
{
public string Message { get; }
public CustomEventArgs(string message)
{
Message = message;
}
}
public class Button
{
public event EventHandler<CustomEventArgs> Clicked;
public void OnClicked(string message)
{
Clicked?.Invoke(this, new CustomEventArgs(message));
}
}
// 使用
var button = new Button();
button.Clicked += (sender, e) =>
Console.WriteLine($"Button clicked with message: {e.Message}");
button.OnClicked("Hello World!");
3. 事件访问器
可以自定义事件的add和remove访问器。
private EventHandler _clickHandlers;
public event EventHandler Click
{
add
{
// 自定义添加逻辑
_clickHandlers = (EventHandler)Delegate.Combine(_clickHandlers, value);
}
remove
{
// 自定义移除逻辑
_clickHandlers = (EventHandler)Delegate.Remove(_clickHandlers, value);
}
}
protected virtual void OnClick()
{
_clickHandlers?.Invoke(this, EventArgs.Empty);
}
三、事件与委托的高级用法
1. 弱事件模式
解决内存泄漏问题,当订阅者被销毁时自动取消订阅。
// 弱事件管理器
public class WeakEventManager<TEventArgs> where TEventArgs : EventArgs
{
private readonly Dictionary<string, WeakReference<EventHandler<TEventArgs>>> _handlers
= new Dictionary<string, WeakReference<EventHandler<TEventArgs>>>();
private readonly object _lock = new object();
public void AddHandler(EventHandler<TEventArgs> handler)
{
lock (_lock)
{
string key = Guid.NewGuid().ToString();
_handlers[key] = new WeakReference<EventHandler<TEventArgs>>(handler);
}
}
public void RemoveHandler(EventHandler<TEventArgs> handler)
{
lock (_lock)
{
foreach (var kvp in _handlers.ToArray())
{
if (kvp.Value.TryGetTarget(out var target) && target == handler)
{
_handlers.Remove(kvp.Key);
}
}
}
}
public void Raise(object sender, TEventArgs e)
{
lock (_lock)
{
foreach (var kvp in _handlers.ToArray())
{
if (kvp.Value.TryGetTarget(out var handler))
{
handler(sender, e);
}
else
{
_handlers.Remove(kvp.Key);
}
}
}
}
}
// 使用
public class Button
{
private readonly WeakEventManager<EventArgs> _clickManager
= new WeakEventManager<EventArgs>();
public event EventHandler Click
{
add => _clickManager.AddHandler(value);
remove => _clickManager.RemoveHandler(value);
}
public void PerformClick()
{
_clickManager.Raise(this, EventArgs.Empty);
}
}
2. 事件聚合器
集中管理多个事件。
public class EventAggregator
{
private readonly Dictionary<Type, List<Delegate>> _handlers
= new Dictionary<Type, List<Delegate>>();
public event EventHandler<MessageEventArgs> MessageReceived;
public void Subscribe<TEvent>(Action<TEvent> handler)
{
var eventType = typeof(TEvent);
if (!_handlers.ContainsKey(eventType))
{
_handlers[eventType] = new List<Delegate>();
}
_handlers[eventType].Add(handler);
}
public void Publish<TEvent>(TEvent eventToPublish)
{
var eventType = typeof(TEvent);
if (_handlers.TryGetValue(eventType, out var handlers))
{
foreach (var handler in handlers.OfType<Action<TEvent>>())
{
handler(eventToPublish);
}
}
}
}
// 使用
var aggregator = new EventAggregator();
aggregator.Subscribe<string>(msg => Console.WriteLine($"Received: {msg}"));
aggregator.Publish("Hello World!");
3. 异步事件处理
public class AsyncButton
{
private readonly SynchronizationContext _context;
public event EventHandler Clicked;
public AsyncButton()
{
_context = SynchronizationContext.Current ??
new SynchronizationContext();
}
public async void OnClickedAsync()
{
// 模拟异步操作
await Task.Delay(1000);
// 在原始上下文中触发事件
_context.Post(_ =>
{
Clicked?.Invoke(this, EventArgs.Empty);
}, null);
}
}
// 使用
var button = new AsyncButton();
button.Clicked += async (sender, e) =>
{
Console.WriteLine("Start handling");
await Task.Delay(500);
Console.WriteLine("Finished handling");
};
button.OnClickedAsync();
四、事件与委托的最佳实践
1. 命名约定
- 委托类型:以
EventHandler
结尾(如ClickEventHandler
) - 事件参数:继承自
EventArgs
- 事件名称:使用过去式(如
Clicked
而不是Clicking
)
2. 线程安全
private EventHandler _clickHandlers;
public event EventHandler Click
{
add
{
lock (_lockObject)
{
_clickHandlers = (EventHandler)Delegate.Combine(_clickHandlers, value);
}
}
remove
{
lock (_lockObject)
{
_clickHandlers = (EventHandler)Delegate.Remove(_clickHandlers, value);
}
}
}
3. 内存管理
// 使用弱引用防止内存泄漏
private WeakReference<EventHandler> _weakHandler;
public event EventHandler Click
{
add
{
_weakHandler = new WeakReference<EventHandler>(value);
}
remove
{
// 实现弱引用移除逻辑
}
}
4. 事件参数设计
public class DataChangedEventArgs : EventArgs
{
public object OldValue { get; }
public object NewValue { get; }
public DataChangedEventArgs(object oldValue, object newValue)
{
OldValue = oldValue;
NewValue = newValue;
}
}
// 使用
public event EventHandler<DataChangedEventArgs> DataChanged;
5. 虚事件模式
public class Control
{
public event EventHandler Click;
protected virtual void OnClick(EventArgs e)
{
Click?.Invoke(this, e);
}
}
public class Button : Control
{
protected override void OnClick(EventArgs e)
{
// 子类可以添加额外逻辑
Console.WriteLine("Button click processing");
base.OnClick(e); // 调用基类实现
}
}
五、常见模式与技巧
1. 取消订阅模式
public class Subscriber : IDisposable
{
private readonly Publisher _publisher;
private EventHandler _handler;
public Subscriber(Publisher publisher)
{
_publisher = publisher;
_handler = OnEvent;
_publisher.EventOccurred += _handler;
}
private void OnEvent(object sender, EventArgs e)
{
Console.WriteLine("Event handled");
}
public void Dispose()
{
_publisher.EventOccurred -= _handler;
}
}
// 使用
using (var sub = new Subscriber(publisher))
{
// 订阅期间处理事件
}
// 自动取消订阅
2. 事件总线模式
public static class EventBus
{
private static readonly Dictionary<Type, List<Delegate>> _handlers
= new Dictionary<Type, List<Delegate>>();
public static void Subscribe<TEvent>(Action<TEvent> handler)
{
var type = typeof(TEvent);
if (!_handlers.ContainsKey(type))
{
_handlers[type] = new List<Delegate>();
}
_handlers[type].Add(handler);
}
public static void Publish<TEvent>(TEvent eventToPublish)
{
var type = typeof(TEvent);
if (_handlers.TryGetValue(type, out var handlers))
{
foreach (var handler in handlers.OfType<Action<TEvent>>())
{
handler(eventToPublish);
}
}
}
}
// 使用
EventBus.Subscribe<string>(msg => Console.WriteLine(msg));
EventBus.Publish("Hello EventBus!");
3. 事件过滤器
public class FilteredEventPublisher
{
private readonly List<EventHandler<CustomEventArgs>> _handlers
= new List<EventHandler<CustomEventArgs>>();
public event EventHandler<CustomEventArgs> Event
{
add => _handlers.Add(value);
remove => _handlers.Remove(value);
}
public void RaiseEvent(CustomEventArgs e, Predicate<CustomEventArgs> filter)
{
foreach (var handler in _handlers)
{
if (filter(e))
{
handler(this, e);
}
}
}
}
// 使用
var publisher = new FilteredEventPublisher();
publisher.Event += (s, e) => Console.WriteLine(e.Message);
publisher.RaiseEvent(new CustomEventArgs("Important"), e => e.IsImportant);
六、性能优化技巧
1. 减少事件处理开销
// 批量处理事件
private List<CustomEventArgs> _eventQueue = new List<CustomEventArgs>();
private readonly object _queueLock = new object();
public event EventHandler<CustomEventArgs> ProcessedEvent;
public void RaiseEvent(CustomEventArgs e)
{
lock (_queueLock)
{
_eventQueue.Add(e);
}
Task.Run(() => ProcessQueue());
}
private void ProcessQueue()
{
while (true)
{
List<CustomEventArgs> eventsToProcess;
lock (_queueLock)
{
if (_eventQueue.Count == 0) break;
eventsToProcess = _eventQueue.ToList();
_eventQueue.Clear();
}
foreach (var e in eventsToProcess)
{
// 处理事件
}
ProcessedEvent?.Invoke(this, new CustomEventArgs("Batch processed"));
}
}
2. 异步事件处理优化
public class AsyncEventPublisher
{
private readonly SynchronizationContext _context;
private event EventHandler<CustomEventArgs> _event;
public AsyncEventPublisher()
{
_context = SynchronizationContext.Current ??
new SynchronizationContext();
}
public event EventHandler<CustomEventArgs> Event
{
add => _event += value;
remove => _event -= value;
}
public void RaiseEventAsync(CustomEventArgs e)
{
Task.Run(() =>
{
// 模拟耗时操作
Thread.Sleep(100);
_context.Post(_ =>
{
_event?.Invoke(this, e);
}, null);
});
}
}
3. 事件缓存
public class CachedEventPublisher
{
private CustomEventArgs _cachedEvent;
private DateTime _cacheTime;
private readonly TimeSpan _cacheDuration = TimeSpan.FromSeconds(5);
public event EventHandler<CustomEventArgs> Event;
public void RaiseEvent(CustomEventArgs e)
{
_cachedEvent = e;
_cacheTime = DateTime.Now;
// 立即触发事件
Event?.Invoke(this, e);
}
public CustomEventArgs GetCachedEvent()
{
if (_cachedEvent != null &&
DateTime.Now - _cacheTime < _cacheDuration)
{
return _cachedEvent;
}
return null;
}
}
七、调试与测试技巧
1. 事件调试
public class DebugEventPublisher
{
public event EventHandler DebugEvent;
public void RaiseDebugEvent()
{
Debug.WriteLine("DebugEvent is about to be raised");
DebugEvent?.Invoke(this, EventArgs.Empty);
Debug.WriteLine("DebugEvent was raised");
}
}
// 使用
var publisher = new DebugEventPublisher();
publisher.DebugEvent += (s, e) => Debug.WriteLine("DebugEvent handled");
publisher.RaiseDebugEvent();
2. 单元测试事件
[TestClass]
public class EventTests
{
[TestMethod]
public void TestEventRaised()
{
// Arrange
var publisher = new EventPublisher();
bool eventRaised = false;
publisher.Event += (s, e) => eventRaised = true;
// Act
publisher.RaiseEvent();
// Assert
Assert.IsTrue(eventRaised);
}
[TestMethod]
public void TestEventArgs()
{
// Arrange
var expected = new CustomEventArgs("Test");
var actual = default(CustomEventArgs);
var publisher = new EventPublisher();
publisher.Event += (s, e) => actual = e;
// Act
publisher.RaiseEvent(expected);
// Assert
Assert.AreEqual(expected.Message, actual.Message);
}
}
八、常见陷阱与解决方案
1. 内存泄漏
问题:订阅者未取消订阅导致对象无法被GC回收。
解决方案:
public class Subscriber : IDisposable
{
private readonly Publisher _publisher;
private EventHandler _handler;
public Subscriber(Publisher publisher)
{
_publisher = publisher;
_handler = OnEvent;
_publisher.Event += _handler;
}
private void OnEvent(object sender, EventArgs e)
{
// 处理事件
}
public void Dispose()
{
_publisher.Event -= _handler;
}
}
// 使用
using (var sub = new Subscriber(publisher))
{
// 订阅期间处理事件
}
// 自动取消订阅
2. 竞态条件
问题:多线程环境下事件订阅/取消订阅可能导致异常。
解决方案:
private readonly object _lock = new object();
private EventHandler _eventHandlers;
public event EventHandler Event
{
add
{
lock (_lock)
{
_eventHandlers = (EventHandler)Delegate.Combine(_eventHandlers, value);
}
}
remove
{
lock (_lock)
{
_eventHandlers = (EventHandler)Delegate.Remove(_eventHandlers, value);
}
}
}
3. 事件参数不一致
问题:不同订阅者期望不同的事件参数。
解决方案:
public class EventPublisher
{
// 基础事件
public event EventHandler<BaseEventArgs> BaseEvent;
// 特定事件
public event EventHandler<SpecificEventArgs> SpecificEvent;
public void RaiseEvents()
{
BaseEvent?.Invoke(this, new BaseEventArgs());
SpecificEvent?.Invoke(this, new SpecificEventArgs());
}
}
// 使用
publisher.BaseEvent += HandleBaseEvent;
publisher.SpecificEvent += HandleSpecificEvent;
九、高级模式
1. 事件聚合器模式
public class EventAggregator
{
private readonly Dictionary<Type, List<Delegate>> _handlers
= new Dictionary<Type, List<Delegate>>();
public void Subscribe<TEvent>(Action<TEvent> handler)
{
var type = typeof(TEvent);
if (!_handlers.ContainsKey(type))
{
_handlers[type] = new List<Delegate>();
}
_handlers[type].Add(handler);
}
public void Publish<TEvent>(TEvent eventToPublish)
{
var type = typeof(TEvent);
if (_handlers.TryGetValue(type, out var handlers))
{
foreach (var handler in handlers.OfType<Action<TEvent>>())
{
handler(eventToPublish);
}
}
}
}
// 使用
var aggregator = new EventAggregator();
aggregator.Subscribe<string>(msg => Console.WriteLine(msg));
aggregator.Publish("Hello Aggregator!");
2. 弱事件模式
public class WeakEventManager<TEventArgs> where TEventArgs : EventArgs
{
private readonly Dictionary<string, WeakReference<EventHandler<TEventArgs>>> _handlers
= new Dictionary<string, WeakReference<EventHandler<TEventArgs>>>();
public void AddHandler(EventHandler<TEventArgs> handler)
{
var key = Guid.NewGuid().ToString();
_handlers[key] = new WeakReference<EventHandler<TEventArgs>>(handler);
}
public void RemoveHandler(EventHandler<TEventArgs> handler)
{
foreach (var kvp in _handlers.ToArray())
{
if (kvp.Value.TryGetTarget(out var target) && target == handler)
{
_handlers.Remove(kvp.Key);
}
}
}
public void Raise(object sender, TEventArgs e)
{
foreach (var kvp in _handlers.ToArray())
{
if (kvp.Value.TryGetTarget(out var handler))
{
handler(sender, e);
}
else
{
_handlers.Remove(kvp.Key);
}
}
}
}
3. 事件总线模式
public static class EventBus
{
private static readonly Dictionary<Type, List<Delegate>> _handlers
= new Dictionary<Type, List<Delegate>>();
public static void Subscribe<TEvent>(Action<TEvent> handler)
{
var type = typeof(TEvent);
if (!_handlers.ContainsKey(type))
{
_handlers[type] = new List<Delegate>();
}
_handlers[type].Add(handler);
}
public static void Publish<TEvent>(TEvent eventToPublish)
{
var type = typeof(TEvent);
if (_handlers.TryGetValue(type, out var handlers))
{
foreach (var handler in handlers.OfType<Action<TEvent>>())
{
handler(eventToPublish);
}
}
}
}
// 使用
EventBus.Subscribe<string>(msg => Console.WriteLine(msg));
EventBus.Publish("Hello EventBus!");
十、总结
- 委托是C#中实现回调机制的基础,
EventHandler
和Func
/Action
是最常用的内置委托类型 - 事件是基于委托的安全发布-订阅机制,遵循标准的
add
/remove
访问器模式 - 最佳实践包括使用标准事件模式、线程安全实现、弱引用防止内存泄漏等
- 高级模式如事件聚合器、事件总线、弱事件等可以解决复杂场景下的通信问题
- 调试技巧包括使用条件断点、日志记录和单元测试验证事件行为