在游戏开发中,消息事件通过委托实现。实际开发中,经常会遇到界面之间、玩家之间的相互通信。本文实现的消息事件封装采用的时监听和分发机制,它主要运用在逻辑判断,减少程序之间的耦合度,而且使用起来非常方便。监听服务器消息也可以运用事件监听机制,收到服务器的信息之后可以执行某个事件。曾经维护过一个项目的网络通信相关逻辑全都写在NetManger中,到后期这个脚本一万多行,维护十分困难。合理的框架结构能够帮助我们降低程序的耦合性,使代码易于维护更新。

第一版不定参数设计

利用不定参params object[] args 我们只需要一个监听、一个移除、一个派发函数就可以完成事件的封装

public delegate void GameEventHandler(params object[] args);
 public static void ListenEvent(System.Enum type, GameEventHandler listener)
        {
            List<GameEventHandler> listeners;
            if (!eventListeners.TryGetValue(type, out listeners))
            {
                listeners = new List<GameEventHandler>();
                eventListeners.Add(type, listeners);
            }
            listeners.Add(listener);
        }

但是这样的设计存在一个问题,因为所有的参数类型都是object类型,不可避免的会有装箱/拆箱的操作

  • 装箱:将值类型转换为引用类型 ,以下是一个简单的装箱的过程
int val = 100; 
object obj = val;
  • 拆箱:将引用类型转换为值类型 ,以下就是简单的拆箱的过程
int val = 100; 
object obj = val; 
int num = (int) obj;
值类型和引用类型
  • 值类型:整型:Int;长整型:long;浮点型:float;字符型:char;布尔型:bool;枚举:enum;结构:struct;它们统一继承 System.ValueType
  • 值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的是堆中存放的地址;
  • 值类型存取快,引用类型存取慢;
  • 值类型表示实际数据,引用类型表示指向内存堆中的数据的指针和引用;
  • 栈的内存是自动释放的,堆内存是.NET中会由GC来自动释放
  • 引用类型:数组,用户定义的类、接口、委托,object,字符串等

当 CLR 对值类型进行装箱时,会将该值包装为 System.Object 类型,再将包装后的对象存储在堆上。 拆箱就是从对象中提取对应的值类型的一个过程。
与简单的赋值操作相比,装箱和拆箱都需要进行大量的数据计算。对值类型进行装箱时,CLR 必须重新分配一个新的对象。拆箱所需的强制转换也需要进行大量的计算,两者相比,仅仅是程度不高,并且也可能会出现类型转换发生的异常情形。

第二版利用泛型重构

其实最初设计时为了代码简洁方便就直接使用了params object[] args,但是在整理代码重构时发现这里涉及到大量的装箱/拆箱的操作,因此还是得利用泛型来省掉这一部的操作。这里根据多年游戏开发的经验,设置了0~4个参数的监听处理。

using System.Collections.Generic;
using System;
  
public class GlobalEvent
{
    private static Dictionary<System.Enum, List<Delegate>> events = new Dictionary<Enum, List<Delegate>>();

    /// <summary>
    /// 添加监听事件
    /// </summary>
    /// <param name="type">监听事件</param>
    /// <param name="listener">监听回调</param>
    public static void Listen(System.Enum type, Action action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
    }
    /// <summary>
    /// 带一个参数的监听
    /// 用泛型避免使用 params object[] args 带来装箱和拆箱的性能损耗
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="action"></param>
    public static void Listen<T>(System.Enum type, Action<T> action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
        
    }

    //两个参数监听回调
    public static void Listen<T,U>(System.Enum type, Action<T,U> action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
    }

    //三个参数监听回调
    public static void Listen<T,U,V>(System.Enum type, Action<T, U,V> action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
    }

    //四个参数监听回调
    public static void Listen<T, U, V,Z>(System.Enum type, Action<T, U, V,Z> action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
    }

    /// <summary>
    /// 移除所有监听
    /// </summary>
    /// <param name="type"></param>
    public static void Remove(System.Enum type)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            events.Remove(type);
        }
    }

    /// <summary>
    /// 移除指定的监听
    /// </summary>
    public static void Remove(System.Enum type, Action action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    public static void Remove<T>(System.Enum type, Action<T> action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    public static void Remove<T,U>(System.Enum type, Action<T,U> action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    public static void Remove<T,U,V>(System.Enum type, Action<T,U,V> action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    public static void Remove<T, U, V,Z>(System.Enum type, Action<T, U, V,Z> action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    /// <summary>
    /// 调用监听事件
    /// </summary>

    public static void Dispatch(System.Enum type)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action action = listener as Action;
                action.Invoke();
            }
        }
    }

    public static void Dispatch<T>(System.Enum type,T arg0)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action<T> action = listener as Action<T>;
                action.Invoke(arg0);
            }
        }
    }

    public static void Dispatch<T,U>(System.Enum type, T arg0,U arg1)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action<T,U> action = listener as Action<T,U>;
                action.Invoke(arg0,arg1);
            }
        }
    }

    public static void Dispatch<T, U,V>(System.Enum type, T arg0, U arg1,V arg2)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action<T, U,V> action = listener as Action<T, U,V>;
                action.Invoke(arg0, arg1,arg2);
            }
        }
    }

    public static void Dispatch<T, U, V,Z>(System.Enum type, T arg0, U arg1, V arg2,Z arg3)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action<T, U, V,Z> action = listener as Action<T, U, V,Z>;
                action.Invoke(arg0, arg1, arg2,arg3);
            }
        }
    }
}