Unity实现支持泛型的事件管理以减少使用object作为参数带来的频繁装拆箱
如果不用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作为参数带来的频繁装拆箱的更多相关文章
- jquery技巧之让任何组件都支持类似DOM的事件管理
本文介绍一个jquery的小技巧,能让任意组件对象都能支持类似DOM的事件管理,也就是说除了派发事件,添加或删除事件监听器,还能支持事件冒泡,阻止事件默认行为等等.在jquery的帮助下,使用这个方法 ...
- Redis事件管理(一)
Redis统一的时间管理器,同时管理文件事件和定时器, 这个管理器的定义: #if defined(__APPLE__) #define HAVE_TASKINFO 1 #endif /* Test ...
- Redis事件管理(三)
Redis的事件管理和定时器的管理都是自己来实现的,Redis的事件管理分为两部分,一部分是封装了系统的异步事件API,还有一部分是在这基础上封装了一个通用的事件管理器,根据具体的系统来决定具体使用哪 ...
- EDK II之DXE Core的事件管理
本文简单介绍一下UEFI中的事件管理: UEFI是不支持多进程的,但是UEFI支持多事件分发机制.UEFI只支持时钟中断,并基于时钟中断实现事件分发.类似于OS中基于时钟中断来实现基于时间片的多任务调 ...
- MUI事件管理
模块:事件管理 http://dev.dcloud.net.cn/mui/event/ 事件绑定: 除了可以使用addEventListener()方法监听某个特定元素上的事件外, 也可以使用.on( ...
- 基于JS的event-manage事件管理库(一步一步实现)
关于文章 最近在提升个人技能的同时,决定把自己为数不多的沉淀记录下来,让自己理解的更加深刻,同时也欢迎各位看官指出不足之处. 随着node.js的盛行,引领着Javascript上天下地无所不能啊,本 ...
- C#规范整理·泛型委托事件
基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托本身是一种引用类型,它保存的也是托管堆中对象的引用,只不过这个引用比较特殊,它是 ...
- 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理
服务器文档下载zip格式 刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...
- DCloud-MUI:事件管理
ylbtech-DCloud-MUI:事件管理 极简的JS函数 1.返回顶部 1.事件绑定 除了可以使用addEventListener()方法监听某个特定元素上的事件外, 也可以使用.on()方法实 ...
随机推荐
- MyBatis:学习笔记(4)——动态SQL
MyBatis:学习笔记(4)——动态SQL
- ubuntu16.04 docker安装
docker官网安装页面:https://docs.docker.com/engine/installation/linux/ubuntu/ 这个是ubuntu14.04 LTS需要的 $ sudo ...
- 算法(Algorithms)第4版 练习 1.5.14
package com.qiusongde; import edu.princeton.cs.algs4.StdIn; import edu.princeton.cs.algs4.StdOut; pu ...
- Storm 执行异常 java.lang.RuntimeException: java.nio.channels.UnresolvedAddressException 问题解决
最近写的 binlog2kafka storm job 上线在一个新的集群环境中(storm 0.9.0.1, kafka 0.8), storm job 运行时报出如下异常: java.lang.R ...
- Hibernate技术
Hibernate中3个重要的类: 配置类(configuration) 负责管理Hibernate的配置信息,包含数据库连接URL.数据库用户.数据库密麻麻.数据库驱动等. 会话工厂类(Sessio ...
- JavaScript 的 async/await
随着 Node 7 的发布,越来越多的人开始研究据说是异步编程终级解决方案的 async/await. 异步编程的最高境界,就是根本不用关心它是不是异步. async 函数就是隧道尽头的亮光,很多人认 ...
- 造成segmentation fault的可能原因分析
一 造成segment fault,产生core dump的可能原因 1.内存访问越界 a) 由于使用错误的下标,导致数组访问越界 b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串 ...
- 9 python 数据类型—字典
字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据.python对key进行哈希函数运算,根据计算的结果决定value的存储地址,所以字典是无序存储的,且key必须是可 ...
- JavaUtil_06_HttpUtil_使用httpclient实现
一.简介 使用 appache 的 httpclient 来实现的 二.源码 package com.ray.weixin.gz.util; import java.io.File; import j ...
- python字符串相关操作
字符串搜索相关搜索指定字符串,没有返回-1:str.find('t')指定起始位置搜索:str.find('t',start)指定起始及结束位置搜索:str.find('t',start,end)从右 ...