unity3d无需进行装箱/拆箱的【事件管理器】

2023-08-24 14:10:24 浏览数 (3)

装箱/拆箱

为了解释“为什么不用object传递参数?”先简单介绍一下“装箱/拆箱”,请看下面代码:

代码语言:javascript复制
int a = 10;
object b = a; //装箱
a = (int)b; //拆箱

第二行,会在堆上实例化一个装箱的对象,并把a的值复制进去,b 引用的就是这个对象。 第三行,会再次进行值复制,若 b 不再引用,则需等待垃圾回收。

常见的事件管理器

我们看一些常见的事件管理器使用代码:

代码语言:javascript复制
void Start()
{
    //注册事件
    EventManager.AddEventListener("Click", OnClick);
}

public void OnClick(object data)
{
    Debug.Log("clickBlock: "   data);
}

//派发事件
EventManager.dispatchEvent("Click", 123);

如上所述,若传递的是引用类型则不会有影响。但如果传递的是值类型,就会产生上述的性能消耗。 泛型优化 我们可以通过泛型去设置参数的类型,从而不需要通过 object 类型传递参数:

public void Dispatch

代码语言:javascript复制
void Start()
{
    //注册事件
    EventDispatcher.global.AddListener<int>("Click", OnClick);
}

public void OnClick(int data)
{
    Debug.Log("clickBlock: "   data);
    Debug.Log(gameObject.name);
}

void OnDestroy()
{
    EventDispatcher.global.RemoveListener<int>("Click",OnClick);
}
//派发事件
EventDispatcher.global.Dispatch<int>("Click", 123);

这里要注意的是一定要GameObject销毁时调用注销委托,不然可能出现如下错误

完整代码

如下:

代码语言:javascript复制
using System;
using System.Collections.Generic;

public class EventDispatcher

{

    public static EventDispatcher global

    {

        get;

        private set;

    }

    static EventDispatcher()

    {

        global = new EventDispatcher();

    }



    private Dictionary<string, Delegate> _listeners = new Dictionary<string, Delegate>();



    public void AddListener<T1, T2, T3, T4>(string evt, Action<T1, T2, T3, T4> callback)

    {

        AddListener(evt, (Delegate)callback);

    }

    public void AddListener<T1, T2, T3>(string evt, Action<T1, T2, T3> callback)

    {

        AddListener(evt, (Delegate)callback);

    }

    public void AddListener<T1, T2>(string evt, Action<T1, T2> callback)

    {

        AddListener(evt, (Delegate)callback);

    }

    public void AddListener<T>(string evt, Action<T> callback)

    {

        AddListener(evt, (Delegate)callback);

    }

    public void AddListener(string evt, Action callback)

    {

        AddListener(evt, (Delegate)callback);

    }

    public void AddListener(string evt, Delegate callback)

    {

        Delegate listener;

        if (_listeners.TryGetValue(evt, out listener))

        {

            _listeners[evt] = Delegate.Combine(listener, callback);

        }

        else

        {

            _listeners[evt] = callback;

        }

    }



    public void RemoveListener<T1, T2, T3, T4>(string evt, Action<T1, T2, T3, T4> callback)

    {

        RemoveListener(evt, (Delegate)callback);

    }

    public void RemoveListener<T1, T2, T3>(string evt, Action<T1, T2, T3> callback)

    {

        RemoveListener(evt, (Delegate)callback);

    }

    public void RemoveListener<T1, T2>(string evt, Action<T1, T2> callback)

    {

        RemoveListener(evt, (Delegate)callback);

    }

    public void RemoveListener<T>(string evt, Action<T> callback)

    {

        RemoveListener(evt, (Delegate)callback);

    }

    public void RemoveListener(string evt, Action callback)

    {

        RemoveListener(evt, (Delegate)callback);

    }

    private void RemoveListener(string evt, Delegate callback)

    {

        Delegate listener;

        if (_listeners.TryGetValue(evt, out listener))

        {

            listener = Delegate.Remove(listener, callback);

            if (listener == null)

            {

                _listeners.Remove(evt);

            }

            else

            {

                _listeners[evt] = listener;

            }

        }

    }



    public void Dispatch<T1, T2, T3, T4>(string evt, T1 arg1, T2 arg2, T3 arg3, T4 arg4)

    {

        Delegate[] methods = GetMethods(evt);

        if (methods != null)

        {

            foreach (Delegate m in methods)

            {

                try

                {
                    if (m.Target != null)
                        ((Action<T1, T2, T3, T4>)m)(arg1, arg2, arg3, arg4);

                }

                catch (Exception e) { LogError(e); }

            }

        }

    }



    public void Dispatch<T1, T2, T3>(string evt, T1 arg1, T2 arg2, T3 arg3)

    {

        Delegate[] methods = GetMethods(evt);

        if (methods != null)

        {

            foreach (Delegate m in methods)

            {

                try

                {
                    if (m.Target != null)
                        ((Action<T1, T2, T3>)m)(arg1, arg2, arg3);

                }

                catch (Exception e) { LogError(e); }

            }

        }

    }



    public void Dispatch<T1, T2>(string evt, T1 arg1, T2 arg2)

    {

        Delegate[] methods = GetMethods(evt);

        if (methods != null)

        {

            foreach (Delegate m in methods)

            {

                try

                {
                    if (m.Target != null)
                        ((Action<T1, T2>)m)(arg1, arg2);

                }

                catch (Exception e) { LogError(e); }

            }

        }

    }



    public void Dispatch<T>(string evt, T arg)

    {

        Delegate[] methods = GetMethods(evt);

        if (methods != null)

        {

            foreach (Delegate m in methods)

            {

                try

                {

                    if (m.Target != null)
                    {
                        ((Action<T>)m)(arg);
                    }

                }

                catch (Exception e) { LogError(e); }

            }

        }

    }



    public void Dispatch(string evt)

    {

        Delegate[] methods = GetMethods(evt);

        if (methods != null)

        {

            foreach (Delegate m in methods)

            {

                try

                {
                    if (m.Target != null)
                        ((Action)m)();

                }

                catch (Exception e) { LogError(e); }

            }

        }

    }



    private Delegate[] GetMethods(string evt)

    {

        Delegate listener;

        if (_listeners.TryGetValue(evt, out listener))

        {

            return listener.GetInvocationList();

        }

        return null;

    }



    private static void LogError(Exception e)
    {

        UnityEngine.Debug.LogError(e);

    }

}

代码分析

订阅消息时

代码语言:javascript复制
_listeners[evt] = Delegate.Combine(listener, callback);

Delegate.Combine将指定的多路广播(可组合)委托的调用列表连接起来,相当于事件的 = 方法

发送消息时

代码语言:javascript复制
listener.GetInvocationList()

GetInvocationList得到链式委托

取消订阅时

代码语言:javascript复制
listener = Delegate.Remove(listener, callback);

Delegate.Remove相当于 -= 方法

0 人点赞