c#高级编程--弱事件

背景

普通.net事件是强引用,会导致内存泄漏,就是监听者被事件源引用了,得不到释放。因此引入弱事件。

通过事件,直接连接到发布程序和侦听器。但垃圾回收有一个问题。例如,如果侦听器不再直接引用,发布程序就仍有一个引用。垃圾回收器不能清空侦听器占用的内存,因为发布程序仍保有一个引用,会针对侦听器触发事件。这种强连接可以通过弱事件模式来解决,即使用WeekEventManager作为发布程序和侦听器之间的中介。

        要使用弱事件,需要创建一个派生自WeekEventManager类的类。WeekEventManager类在程序集WindowsBase的名称空间System.Windows中定义。对于弱事件模式,弱事件管理器类需要静态方法AddListener()和StopListening()。侦听器使用这些方法连接发布程序,和断开与发布程序的连接,而不是直接使用发布程序中的事件。可以通过AddListener()和RemoveListener()方法,调用WeekEventManager基类中的方法,来添加和删除侦听器。

        弱事件管理器还需要重写基类的StartListening()和StopListening()方法。添加一个侦听器时调用StartListening()方法,删除最后一个侦听器时调用StopListening()方法。DeliverEvent()方法在侦听器中调用IWeekEventLister接口中的ReceiveWeekEvent()方法。侦听器需要实现IWeekEventListener接口,这个接口定义了ReceiveWeekEvent()方法,触发事件时,从弱事件管理器中调用这个方法。

自定义弱事件

 

 按顺序点击按钮,先初始化两个订阅类,并绑定publisher事件

然后将两个订阅者置为null,并执行GC

此时按道理来说,订阅者应该收不到消息了,因为已经被回收了。

但是因为publisher.event += subscriber.xxx

实际上导致 subscriber被人引用了,所以实际上是无法回收的。

通过第四步,手动执行事件,可以看出普通的订阅类依旧 还是会收到消息通知

但是继承了弱事件接口(IweakListener)的类,确实被回收了。

所以综上所述,弱引用模式引入,可以解决一些事件订阅后,我们要销毁订阅者类,但是实际没被回收的内存泄漏问题。

缺点:

需要引入一个接口,和一个管理类,代码结构上有些繁琐,而且看了下管理类的源码,内部较为复杂且使用了较多的反射

个人还是建议直接用减等于这种显示的写法, publisher.event -= subscriber.xxx 。

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;
        }
    }
 
 
}

 

WPF中的弱事件

    private class RequerySuggestedEventManager : WeakEventManager
        {
            #region Constructors

            //
            //  Constructors
            //

            private RequerySuggestedEventManager()
            {
            }

            #endregion Constructors

            #region Public Methods

            //
            //  Public Methods
            //

            /// <summary>
            /// Add a handler for the given source's event.
            /// </summary>
            public static void AddHandler(CommandManager source, EventHandler handler)
            {
                if (handler == null)
                    return; // 4.0-compat;  should be:  throw new ArgumentNullException("handler");

                CurrentManager.ProtectedAddHandler(source, handler);
            }

            /// <summary>
            /// Remove a handler for the given source's event.
            /// </summary>
            public static void RemoveHandler(CommandManager source, EventHandler handler)
            {
                if (handler == null)
                    return; // 4.0-compat;  should be:  throw new ArgumentNullException("handler");

                CurrentManager.ProtectedRemoveHandler(source, handler);
            }

            #endregion Public Methods

            #region Protected Methods

            //
            //  Protected Methods
            //

            /// <summary>
            /// Return a new list to hold listeners to the event.
            /// </summary>
            protected override ListenerList NewListenerList()
            {
                return new ListenerList();
            }

            /// <summary>
            /// Listen to the given source for the event.
            /// </summary>
            protected override void StartListening(object source)
            {
                CommandManager typedSource = CommandManager.Current;
                typedSource.PrivateRequerySuggested += new EventHandler(OnRequerySuggested);
            }

            /// <summary>
            /// Stop listening to the given source for the event.
            /// </summary>
            protected override void StopListening(object source)
            {
                CommandManager typedSource = CommandManager.Current;
                typedSource.PrivateRequerySuggested -= new EventHandler(OnRequerySuggested);
            }

            #endregion Protected Methods

            #region Private Properties

            //
            //  Private Properties
            //

            // get the event manager for the current thread
            private static RequerySuggestedEventManager CurrentManager
            {
                get
                {
                    Type managerType = typeof(RequerySuggestedEventManager);
                    RequerySuggestedEventManager manager = (RequerySuggestedEventManager)GetCurrentManager(managerType);

                    // at first use, create and register a new manager
                    if (manager == null)
                    {
                        manager = new RequerySuggestedEventManager();
                        SetCurrentManager(managerType, manager);
                    }

                    return manager;
                }
            }

            #endregion Private Properties

            #region Private Methods

            //
            //  Private Methods
            //

            // event handler for CurrentChanged event
            private void OnRequerySuggested(object sender, EventArgs args)
            {
                DeliverEvent(sender, args);
            }

            #endregion Private Methods
        }

 

posted @ 2022-07-27 19:30  小林野夫  阅读(572)  评论(0编辑  收藏  举报
原文链接:https://www.cnblogs.com/cdaniu/