弱事件应用 WeakEvent

目的

实例在失去作用域后能被回收,实例引用的事件也能自动释放掉;

场景

  1. 用到了一个现有的类型(发布者,无法修改源码);
  2. 无法决定什么时候释放此类型(发布者)的实例;
  3. 一旦监听此类型事件,将产生内存泄漏,导致自己的类型的实例(订阅者)无法释放。

用法

  1. 发布者层(可以修改源码时),事件源定义事件时候用弱事件WeakEvent,这可以使得此事件不会强引用事件的订阅者;

注意:WeakEvent通过委托得到目标实例对象,委托不能是静态方法,即订阅者必须是一个对象,否则会导致null异常;

  1. 订阅者层,使用弱事件中继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;

注意:
WeakEventsWeakEventRelay需要启用可空引用

.NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
.NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
.NET 设计一套高性能的弱事件机制

原文地址:https://www.cnblogs.com/wesson2019-blog/p/14639952.html