目的
实例在失去作用域后能被回收,实例引用的事件也能自动释放掉;
场景
- 用到了一个现有的类型(发布者,无法修改源码);
- 无法决定什么时候释放此类型(发布者)的实例;
- 一旦监听此类型事件,将产生内存泄漏,导致自己的类型的实例(订阅者)无法释放。
用法
- 发布者层(可以修改源码时),事件源定义事件时候用弱事件WeakEvent,这可以使得此事件不会强引用事件的订阅者;
注意:WeakEvent通过委托得到目标实例对象,委托不能是静态方法,即订阅者必须是一个对象,否则会导致null异常;
- 订阅者层,使用弱事件中继WeakEventRelay,为发布者提供弱事件支持;
注意:WeakEventRelay中继发布者时,重定义的委托事件的方法标识要保持不变,以确保事件与处理事件的订阅方法相关联;
用法1
发布者内部封装,外部+=正常调用;
单个参数
原型
// Publisher
public event Action<string> MyEvent;
// Subscriber
var publisher = new PublisherClass();
publisher.MyEvent += PublisherClass_MyEvent;
// Invoke
private void PublisherClass_MyEvent(string arg){ }
转换
private readonly WeakEvent<string> _myEvent = new WeakEvent<string>();
public event EventHandler<string> MyEvent
{
add => _myEvent.Add(value, value.Invoke);
remove => _myEvent.Remove(value);
}
private void OnMyEvent()
{
_myEvent.Invoke(this, "");
}
Demo
// Subscriber
var publisher = new PublisherClass();
publisher.MyEvent += PublisherClass_MyEvent;
private void PublisherClass_MyEvent(object sender, string arg)
{
}
多个参数
原型
public event Action<string, int> MyEvent;
转换
private readonly WeakEvent<MyEventArgs> _myEvent = new WeakEvent<MyEventArgs>();
public event EventHandler<MyEventArgs> MyEvent
{
add => _myEvent.Add(value, value.Invoke);
remove => _myEvent.Remove(value);
}
private void OnMyEvent()
{
_myEvent.Invoke(this, new MyEventArgs("", 1));
}
// 事件生成的数据
public class MyEventArgs : EventArgs
{
public MyEventArgs(string arg1, int arg2)
{
Arg1 = arg1;
Arg2 = arg2;
}
public string Arg1 { get; }
public int Arg2 { get; }
}
Demo
var publisher = new PublisherClass();
publisher.MyEvent += PublisherClass_MyEvent;
private void PublisherClass_MyEvent(object sender, MyEventArgs e)
{
}
用法2
发布者外部中继,+=正常调用;
原型
XXX publisher = new XXX(){}
publisher.EventName1 += XXX_EventName1;
private void XXX_EventName1(object sender, XXEventArgs e) { }
转换
// 订阅者引用弱事件中继,对应需要被弱化的事件源类型(发布者)
internal sealed class XXXWeakEventRelay : WeakEventRelay<XXX>
{
// 重载
public XXXWeakEventRelay(XXX eventSource) : base(eventSource)
{
}
// 弱事件字段,泛型参数是发布者事件参数的类型,而不是事件处理委托类型
private readonly WeakEvent<XXEventArgs> _eventName1 = new WeakEvent<XXEventArgs>();
// 对外公开的事件,同发布者定义的事件
public event XXEventHandler EventName1
{
add => Subscribe(o => o.EventName1 += OnEventName1, () => _eventName1.Add(value, value.Invoke));
remove => _eventName1.Remove(value);
}
// 事件处理函数
private void OnEventName1(object sender, XXEventArgs e)
{
TryInvoke(_eventName1, sender, e);
}
// 订阅者实例被回收后,确保注销发布者中的事件
protected override void OnReferenceLost(XXX source)
{
source.EventName1 -= OnEventName1;
// 其他中继的事件注销
}
}
Demo
var weakEvent = new XXXWeakEventRelay(publisher);
weakEvent.EventName1 += XXX_EventName1;
注意:
WeakEvents、WeakEventRelay需要启用可空引用;
.NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
.NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
.NET 设计一套高性能的弱事件机制