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()方法实 ...
随机推荐
- c++之默认参数的函数
默认参数,看个例子就明白了 int add(int a=5,int b=6,z=3): int main(){ add():// 全部默认 add(1,5)://第三个参数默认 add(1,2,3): ...
- windows与VMware ubuntu虚拟机实现文件共享
本文基本摘自:无限挑战者的博客 本人用的系统是win10系统,应该区别不大,实现的是win10系统和VMWare虚拟机Ubantu系统之间的文件共享.到网上找了很多的方法各种各样的方式,由于对Linu ...
- 希尔排序(Shell Sort)
一.思路 希尔排序是基于插入排序算法,通过允许不相邻的元素进行交换这一简单的改进,使数组变为局部有序,最终再用插入排序. 希尔排序的思想是使数组中任意间隔h的元素都是有序的.这样的数组被称为h有序数组 ...
- BZOJ 1965 [Ahoi2005]SHUFFLE 洗牌:快速幂 + 逆元
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1965 题意: 对于扑克牌的一次洗牌是这样定义的,将一叠N(N为偶数)张扑克牌平均分成上下两 ...
- scanf ---------未完待续
1.不可读入空格 #include<iostream> #include<stdio.h> using namespace std; int main() { char c[5 ...
- Windows cmd findstr
/********************************************************************************** * Windows cmd fi ...
- Metasploit的使用下篇——漏洞攻击
一.上文总结 上篇当中主要通过开启的端口对目标主机进行攻击,最后成功做到了连接不过却不能查看到telnet的登陆用户名和密码,目前来看有两个原因:第一.我对于靶机telnet的设置有问题,没有设置相应 ...
- Unity3D中的Coroutine及其使用(延时、定时调用函数)
http://blog.csdn.net/nizihabi/article/details/47606887 一.Coroutine(协程)的概念和本质 在网上的一些资料当中,一直将Coroutine ...
- 查找图像中椭圆轮廓的快速随机hough变换
查找图像中椭圆轮廓的快速随机hough变换 图像中椭圆轮廓的查找在视频监控等领域有着广泛的应用,经典hough变换给我们提供了一种查找各种图形轮廓的方法,特别是在直线查找方面具有非常高的精确度.但是由 ...
- 测试你的浏览器是否支持WebGL(Does My Browser Support WebGL)
测试你的浏览器是否支持WebGL(Does My Browser Support WebGL) 关于WebGL:WebGL是一种3D绘图标准,这种绘图技术标准允许把JavaScript和OpenGL ...