using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
namespace weakEventTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// 消息发布者
Publisher publisher = new Publisher();
// 普通消息订阅者
Subscriber normalSubscriber = null;
// 弱事件消息订阅者
Subscriber weakSubscriber = null;
private void btnNormal_Click(object sender, EventArgs e)
{
// 创建普通消息订阅者,并绑定到事件源
normalSubscriber = new Subscriber("普通订阅者");
publisher.SampleEvent += normalSubscriber.Receiver;
Console.WriteLine("普通订阅者初始化并绑定事件");
}
private void btnWeakEvent_Click(object sender, EventArgs e)
{
// 创建弱事件消息订阅者,并添加到事件源
weakSubscriber = new Subscriber("弱引用订阅者");
WeakCarInfoEventManager.AddListener(publisher, weakSubscriber);
Console.WriteLine("弱引用订阅者初始化并绑定事件");
}
private void btnGC_Click(object sender, EventArgs e)
{
// 解除注册,GC将可以成功回收对象
//publisher.SampleEvent -= normalSubscriber.Receiver;
// 尝试将普通事件订阅者销毁并使用GC回收,实际GC没有将对象回收
normalSubscriber = null;
Console.WriteLine("将普通订阅者置为null");
// 尝试将弱事件订阅者销毁,并使用GC回收,GC成功将对象回收
weakSubscriber = null;
Console.WriteLine("将弱引用订阅者置为null");
GC.Collect();
}
private void btnRiseEvent_Click(object sender, EventArgs e)
{
// 触发事件
publisher.RaiseEvent();
Console.WriteLine("手动触发发布者事件");
}
}
/// <summary>
/// 事件发布者
/// </summary>
public class Publisher
{
public event EventHandler<EventArgs> SampleEvent;
public virtual void RaiseEvent()
{
SampleEvent?.Invoke(this,new EventArgs());
}
}
/// <summary>
/// 事件订阅者
/// </summary>
public class Subscriber : IWeakEventListener
{
string SubName = "";
public Subscriber(string name) { SubName = name; }
public void Receiver(object sender, EventArgs e)
{
Console.WriteLine("Subscriber: " + SubName + "接受到了事件通知");
}
//通过该方法来处理弱事件管理器推送过来的订阅信息
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
Receiver(sender,e);
return true;
}
}
/// <summary>
/// 事件弱管理器
/// </summary>
public class WeakCarInfoEventManager : WeakEventManager
{
/// <summary>
/// 将订阅者添加到事件源
/// </summary>
/// <param name="source">发布者</param>
/// <param name="listener">订阅者</param>
public static void AddListener(object source, IWeakEventListener listener)
{
CurrentManager.ProtectedAddListener(source, listener);
}
public static void RemoveListener(object source, IWeakEventListener listener)
{
CurrentManager.ProtectedRemoveListener(source, listener);
}
/// <summary>
/// 管理器
/// </summary>
public static WeakCarInfoEventManager CurrentManager
{
get
{
//从现有管理器中获取默认管理器对象
var manager = GetCurrentManager(typeof(WeakCarInfoEventManager))
as WeakCarInfoEventManager;
if (manager == null)
{
manager = new WeakCarInfoEventManager();
//将新建的管理器对象设置为当前类型默认管理器
SetCurrentManager(typeof(WeakCarInfoEventManager), manager);
}
return manager;
}
}
protected override void StartListening(object source)
{
//将发布者事件订阅到当前
(source as Publisher).SampleEvent += CarDealer_NewCarInfo;
}
void CarDealer_NewCarInfo(object sender, EventArgs e)
{
DeliverEvent(sender, e);
}
protected override void StopListening(object source)
{
(source as Publisher).SampleEvent -= CarDealer_NewCarInfo;
}
}
}
如上,winform代码.
界面如下:
按顺序点击按钮,先初始化两个订阅类,并绑定publisher事件
然后将两个订阅者置为null,并执行GC
此时按道理来说,订阅者应该收不到消息了,因为已经被回收了。
但是因为publisher.event += subscriber.xxx
实际上导致 subscriber被人引用了,所以实际上是无法回收的。
通过第四步,手动执行事件,可以看出普通的订阅类依旧 还是会收到消息通知
但是继承了弱事件接口(IweakListener)的类,确实被回收了。
所以综上所述,弱引用模式引入,可以解决一些事件订阅后,我们要销毁订阅者类,但是实际没被回收的内存泄漏问题。
缺点:
需要引入一个接口,和一个管理类,代码结构上有些繁琐,而且看了下管理类的源码,内部较为复杂且使用了较多的反射
个人还是建议直接用减等于这种显示的写法, publisher.event -= subscriber.xxx 。