使用泛型来避免观察者模式—EventCenter的装箱拆箱

  下面是之前实现的一个基于观察者模式事件中心模块,该模块会在应该被监听的类的方法中调用EventTrigger事件触发方法,并传入一个名字和任意一个参数到事件中心。事件中心会根据自己的eventDic事件字典(事件容器)去判断是否有其他方法监听了该事件,若存在监听则调用其中的方法。代码如下:

        public class EventCenter : ISingleton
    {
        public static EventCenter Instance => Singleton<EventCenter>.Instance;

        /// <summary>
        /// 事件容器
        /// </summary>
        private Dictionary<string,UnityAction<object>> eventDic=new Dictionary<string, UnityAction<object>>();
        public void Init()
        {

        }

        /// <summary>
        /// 添加事件监听
        /// </summary>
        /// <param name="name">事件名字</param>
        /// <param name="action">准备用来处理事件的委托函数</param>
        public void AddEventListener(string name ,UnityAction<object> action) {
            if (eventDic.ContainsKey(name))
            {
                eventDic[name] += action;
            }
            else
            {
                eventDic.Add(name, action);
            }
        }

        /// <summary>
        /// 事件触发
        /// </summary>
        /// <param name="name"></param>
        public void EventTrigger(string name,object info) {
            if (eventDic.ContainsKey(name))
            {
                eventDic[name].Invoke(info);
            }
        }

        /// <summary>
        /// 移除事件监听
        /// </summary>
        /// <param name="name">事件名字</param>
        /// <param name="action">委托函数</param>
        public void RemoveEventListener(string name,UnityAction<object> action) {
            if (eventDic.ContainsKey(name))
                eventDic[name]-=action;        
        }

        /// <summary>
        /// 清空事件中心
        /// </summary>
        public void Clear() { 
            eventDic.Clear();
        }
    }

但是这种方法存在缺陷,UnityAction委托中的函数会存在一个参数,这个参数是在事件拥有者Monster类中被传进EventTrigger但是由于Tirgger中使用的参数类型时Object类,所以在将参数分发到各个订阅函数中去时会有引用类型向值类型转换也就是拆箱发生。这样十分损耗性能,可以使用面向对象的思想对UnityAction进行一层封装,然后定义一个空接口去用来存放对于UnityAction 的封装。

首先我们声明一个空接口IEventInfo

     public interface IEventInfo { }

然后封装UnityAction<T>,并提供一个构造函数用于之后往委托中挂载函数

         public class EventInfo<T> :IEventInfo
    { 
        public UnityAction<T> actions;
        public EventInfo(UnityAction<T> action)
        {
            actions += action;
        }
    }

将事件中心里的事件容器字典的值类型改为IEeventInfo

     private Dictionary<string, IEventInfo> eventDic=new Dictionary<string, IEventInfo>();

然后只需要在各方法使用字典容器的时候取出值将其转换为EventInfo即可

        /// <summary>
        /// 添加事件监听
        /// </summary>
        /// <typeparam name="T">监听函数的参数类型</typeparam>
        /// <param name="name">事件名</param>
        /// <param name="action">准备用来处理事件的委托函数</param>
        public void AddEventListener<T>(string name ,UnityAction<T> action) {
            if (eventDic.ContainsKey(name))
            {
                (eventDic[name] as EventInfo<T>).actions += action;
            }
            else
            {
                //eventDic.Add(name, action);
                eventDic.Add(name, new EventInfo<T>(action));
            }
        }

        /// <summary>
        /// 事件触发
        /// </summary>
        /// <typeparam name="T">监听函数的参数类型</typeparam>
        /// <param name="name">事件名</param>
        /// <param name="info">传递的参数</param>
        public void EventTrigger<T>(string name,T info) {
            if (eventDic.ContainsKey(name))
            {
                if ((eventDic[name] as EventInfo<T>).actions!=null)
                    (eventDic[name] as EventInfo<T>).actions.Invoke(info);
            }
        }

        /// <summary>
        /// 移除事件监听
        /// </summary>
        /// <typeparam name="T">监听函数的参数类型</typeparam>
        /// <param name="name">事件名</param>
        /// <param name="action">委托函数</param>
        public void RemoveEventListener<T>(string name,UnityAction<T> action) {
            if (eventDic.ContainsKey(name))
                (eventDic[name] as EventInfo<T>).actions -= action;       
        }

以上即是对于事件中心的优化

猜你喜欢

转载自blog.csdn.net/qq_52690206/article/details/128899569
今日推荐