如果不用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. HDU - 2709 Sumsets 【递推】

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2709 题意 给出一个数N 要求有多少种方式 求和 能够等于N 加的数 必须是 2的幂次 思路 首先可以 ...

  2. c的详细学习(1)C语言概述

        本节用来简要介绍c语言.     (1)C语言的特点: C语言是一种集汇编语言及高级语言为一身的,面向过程的结构化和模块化的程序设计语言. 特点: 兼具高级语言与低级语言的双重能力.C语言允许 ...

  3. 【leetcode刷题笔记】Same Tree

    Given two binary trees, write a function to check if they are equal or not. Two binary trees are con ...

  4. Ubuntu15.10下***搭建及GUI客户端安装

    1.依赖包安装 sudo apt-get install python-pip python-dev build-essential sudo pip install pip sudo apt-get ...

  5. while 读取文件内容

    exec < filename while read line;do echo $line done 方法1 while read line;do echo $line done<$ 方法 ...

  6. LINQ 学习路程 -- 查询操作 OrderBy & OrderByDescending

    Sorting Operator Description OrderBy 通过给定的字段进行升序 降序 排序 OrderByDescending 通过给定字段进行降序排序,仅在方法查询中使用 Then ...

  7. HDU 2089 不要62:数位dp

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意: 问你在区间[n,m]中,有多少个数字不含"4"且不含"62 ...

  8. 分享知识-快乐自己:mongodb 安装部署(linux)

    1):下载 mongodb 包 [root@admin tools]# wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6. ...

  9. PHP+MySQL百万级数据插入的优化

    插入分析 MySQL中插入一个记录需要的时间由下列因素组成,其中的数字表示大约比例: 连接:(3) 发送查询给服务器:(2) 分析查询:(2) 插入记录:(1x记录大小) 插入索引:(1x索引) 关闭 ...

  10. linux命令学习笔记(55):traceroute命令

    通过traceroute我们可以知道信息从你的计算机到互联网另一端的主机是走的什么路径.当然每次数据包 由某一同样的出发点(source)到达某一同样的目的地(destination)走的路径可能会不 ...