如果不用C#自身的event关键字而是要自己实现一个可统一管理游戏中各种消息事件通知管理的系统模块EventManger时,通常都是把事件delegate的参数定义为object类型以适应所有的数据类型,然而这样做的后果就是在使用过程中存在很频繁的装拆箱操作。
实际是有办法实现支持泛型的事件管理的,关键点在于所有形式的delegate方法都是可以保存在类型为Delegate的变量上的,保存和调用时将Delegate强转为目标delegate就行了。简单示例如下:

     public delegate void Act ();
public delegate void Act<T, U>(T t, U u); Dictionary<int, Delegate> eventTable = new Dictionary<int, Delegate>(); public void AddListener<T, U>(int eventType, Act<T, U> listenerBeingAdded)
{
if (!eventTable.ContainsKey(eventType))
{
eventTable.Add(eventType, null);
} Delegate d = eventTable[eventType];
if (d != null && d.GetType() != listenerBeingAdded.GetType())
{
Debug.LogError(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));
}
else
{
eventTable[eventType] = (Act<T, U>)eventTable[eventType] + listenerBeingAdded;
}
} public void RemoveListen<T, U>(int eventType, Act<T, U> listenerBeingRemoved)
{
if (eventTable.ContainsKey(eventType))
{
Delegate d = eventTable[eventType]; if (d == null)
{
Debug.LogError(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType));
}
else if (d.GetType() != listenerBeingRemoved.GetType())
{
Debug.LogError(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
}
else
{
eventTable[eventType] = (Act<T, U>)eventTable[eventType] - listenerBeingRemoved;
if (eventTable[eventType] == null)
{
eventTable.Remove(eventType);
}
}
}
else
{
Debug.LogError(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType));
}
} public void Dispatch<T, U>(int eventType, T param1, U param2)
{
Delegate d;
if (eventTable.TryGetValue(eventType, out d))
{
((Act<T, U>)d)(param1, param2);
}
} [ContextMenu("Test")]
void Test ()
{
AddListener<int, string>(, MyCallback);
Dispatch(, , "");
RemoveListen<int, string>(, MyCallback);
} private void MyCallback (int n, string s)
{
Debug.Log(string.Format("param1 {0}, parma2 {1}", n, s));
}

预定义多个不同参数个数的delegate,再分别重载几个Add、Remove、Dispatch支持不同类型delegate的方法就可以实现整套支持不同参数类型不同参数个数的消息管理功能了。

以上方法可以完全避免参数传递之间的拆装箱,但是稍微有点麻烦之处在于需要重载很多Add、Remove、Dispatch函数。有个简单点的作法是直接将Delegate作为这三个函数的参数而不是具体的delegate,但调用时直接传入具名函数是不能自动转换为Delegate的,需要对每个delegate作个简单的封装,具体如下:

 public delegate void Act ();
public delegate void Act<T, U>(T t, U u); Dictionary<int, Delegate> eventTable = new Dictionary<int, Delegate>(); public void AddListener (int eventType, Delegate listenerBeingAdded)
{
if (!eventTable.ContainsKey(eventType))
{
eventTable.Add(eventType, null);
}
Delegate d = eventTable[eventType];
if (d != null && d.GetType() != listenerBeingAdded.GetType())
{
Debug.LogError(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType(), listenerBeingAdded.GetType()));
}
else
{
eventTable[eventType] = Delegate.Combine(eventTable[eventType], listenerBeingAdded);
}
} public void RemoveListen (int eventType, Delegate listenerBeingRemoved)
{
if (eventTable.ContainsKey(eventType))
{
Delegate d = eventTable[eventType];
if (d == null)
{
Debug.LogError(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType));
}
else if (d.GetType() != listenerBeingRemoved.GetType())
{
Debug.LogError(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
}
else
{
eventTable[eventType] = Delegate.Remove(eventTable[eventType], listenerBeingRemoved);
if (eventTable[eventType] == null)
{
eventTable.Remove(eventType);
}
}
}
else
{
Debug.LogError(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType));
}
} public void Dispatch (int eventType, params object[] n)
{
Delegate d;
if (eventTable.TryGetValue(eventType, out d))
{
d.DynamicInvoke(n);
}
} [ContextMenu("Test")]
void Test ()
{
AddListener(, ToDelegate<int, string>(MyCallback));
AddListener(, ToDelegate(MyCallback1));
//或者
//Delegate dele = new Action<int, string>(MyCallback);
//AddListener(1, dele);
Dispatch(, , "");
RemoveListen(, ToDelegate<int, string>(MyCallback));
//RemoveListen(1, dele);
} private Act<T, U> ToDelegate<T, U>(Act<T, U> act) { return act; }
private Act ToDelegate(Act act) { return act; } private void MyCallback (int n, string s)
{
Debug.Log(string.Format("param1 {0}, parma2 {1}", n, s));
} private void MyCallback1 ()
{
Debug.Log("no param");
}

此方法就需要对每个delegate写个简单的Getter函数(不同参数个数的原型都需要分别包装),或者new一个Action类似的Delegate(不同参数个数直接指明即可),让调用者自己给出具体的函数类别。相比方法1可以省去不少Add、Remove、Dispatch重载代码,但调用者调用时变得稍麻烦一些,同时由于Dispatch只能接受object[]参数导致了拆装箱,故还是推荐方法1。

Unity实现支持泛型的事件管理以减少使用object作为参数带来的频繁装拆箱的更多相关文章

  1. jquery技巧之让任何组件都支持类似DOM的事件管理

    本文介绍一个jquery的小技巧,能让任意组件对象都能支持类似DOM的事件管理,也就是说除了派发事件,添加或删除事件监听器,还能支持事件冒泡,阻止事件默认行为等等.在jquery的帮助下,使用这个方法 ...

  2. Redis事件管理(一)

    Redis统一的时间管理器,同时管理文件事件和定时器, 这个管理器的定义: #if defined(__APPLE__) #define HAVE_TASKINFO 1 #endif /* Test ...

  3. Redis事件管理(三)

    Redis的事件管理和定时器的管理都是自己来实现的,Redis的事件管理分为两部分,一部分是封装了系统的异步事件API,还有一部分是在这基础上封装了一个通用的事件管理器,根据具体的系统来决定具体使用哪 ...

  4. EDK II之DXE Core的事件管理

    本文简单介绍一下UEFI中的事件管理: UEFI是不支持多进程的,但是UEFI支持多事件分发机制.UEFI只支持时钟中断,并基于时钟中断实现事件分发.类似于OS中基于时钟中断来实现基于时间片的多任务调 ...

  5. MUI事件管理

    模块:事件管理 http://dev.dcloud.net.cn/mui/event/ 事件绑定: 除了可以使用addEventListener()方法监听某个特定元素上的事件外, 也可以使用.on( ...

  6. 基于JS的event-manage事件管理库(一步一步实现)

    关于文章 最近在提升个人技能的同时,决定把自己为数不多的沉淀记录下来,让自己理解的更加深刻,同时也欢迎各位看官指出不足之处. 随着node.js的盛行,引领着Javascript上天下地无所不能啊,本 ...

  7. C#规范整理·泛型委托事件

    基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托本身是一种引用类型,它保存的也是托管堆中对象的引用,只不过这个引用比较特殊,它是 ...

  8. 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理

    服务器文档下载zip格式   刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...

  9. DCloud-MUI:事件管理

    ylbtech-DCloud-MUI:事件管理 极简的JS函数 1.返回顶部 1.事件绑定 除了可以使用addEventListener()方法监听某个特定元素上的事件外, 也可以使用.on()方法实 ...

随机推荐

  1. 装箱问题【STL】

    7-9 装箱问题(20 分) 假设有N项物品,大小分别为s​1​​.s​2​​.-.s​i​​.-.s​N​​,其中s​i​​为满足1≤s​i​​≤100的整数.要把这些物品装入到容量为100的一批箱 ...

  2. iOS 动态修改导航栏颜色 UINavigationBar

    示例 所谓动态修改  意思是 在当前页面滚动的过程中 亦或 是在 触发返回事件\进入一个新的页面  导航栏的动态变化 由于系统级别的navBar 高度集成  很多自己想实现的功能 很不好弄 如果是通过 ...

  3. jps参数

    jps是jdk1.5提供的用来显示当前java进程的指令,能显示进程id及进程名称(多指jar包名称.class名称) -q 只显示进程ID -m 显示传递给main方法的参数 -l 显示应用程序完整 ...

  4. codeforces 154A 贪心

    贪心 题目自身限制每个字母最多出现在一个限制词语中,给出k个限制词语,将问题转化为k个子问题,对每个限制词语遍历给出的字符串,如限制词do,若出现连续的oddoood(连续的o和d),统计o和d出现的 ...

  5. Ubuntu14 下安装jdk1.8

    1.oracle官网下载jdk-8u45-linux-i586.gz 此为32位 2.放在任意目录下,如/home/hongxf 3.直接解压该文件 sudo tar -xvf jdk-8u45-li ...

  6. Nginx Rewrite语法详解

    重写中用到的指令 if  (条件) {}  设定条件,再进行重写 set #设置变量 return #返回状态码  return 403; break #跳出rewrite rewrite #重写 I ...

  7. Victor/ArrayList/LinkedList/Stack/CopyOnWriteArrayList 区别

    Victor:采用数组的方式存储数据,与ArrayList相同,线程安全.性能比ArrayList差 ArrayList:采用数据的方式存储数据,线程不安全.ArrayList使用数组来存储数据,使用 ...

  8. Codeforces Round #250 (Div. 2) A, B, C

    A. The Child and Homework time limit per test 1 second memory limit per test 256 megabytes input sta ...

  9. KISSY(JS)炫动导航,缓动应用实例(^_^)

    一个基于KISSY的简单的动画导航,效果还不错,有点像flash的效果.鼠标移到每一个连接上,背景滑块会迅速移到该链接下方,同时平滑改变大小,自适应链接尺寸,并伴随来回的轻微波动,动感相当不错,呵呵, ...

  10. Linux课程---11、Linux中软件安装和调试

    Linux课程---11.Linux中软件安装和调试 一.总结 一句话总结: 启动过程:1.安装软件,2.修改配置文件,3.启动服务 查看过程:4.查看进程,5.查看端口 关闭过程:6.关闭软件,7. ...