一般项目里都会有一个MessageManager用管理全局消息,以实现代码上的解耦。一般来说,代码由三部分组成,监听消息(AddListener),取消监听(RemoveListener),消息分发(Broadcast)。随着代码量的增加,一个类中可能会出现非常多的AddListener/RemoveListener代码,并且经常会有人在监听后忘记Remove。于是,就想利用c#的特性来实现一个消息的自动注册机。
完整代码消息管理器及自动注册机
1.性能分析
原先的Messenger是使用Delegate来管理注册的方法的,要改为反射第一个想到的就是MethodInfo,都知道反射性能差,测了得到以下结果。
调用1000万次空函数 | 耗时/ms |
---|---|
直接调用函数 | 36 |
使用委托调用函数 | 38 |
使用MethodInfo调用函数 | 3492 |
可以看到差距还是很大的,虽然如果换成非空函数后差距会随着函数本身的消耗增加而缩小,但还是想节省这部分的性能开销。
接着发现Delegate 提供了通过委托类型和MethodInfo创建委托的方法,如下:
public static Delegate CreateDelegate(Type type, MethodInfo method)
public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method)
接着是泛型委托的类型如何获取,如下:
public virtual Type MakeGenericType(params Type[] typeArguments)
typeof(Action<>).MakeGenericType(typeof(int));// Action<int>
typeof(Action<,>).MakeGenericType(typeof(int), typeof(int));// Action<int, string>
这样一来,所有问题都解决的,可以动手写了。
2.代码实现
完整代码消息管理器及自动注册机
MessageManager的代码就不放这里了,RegisterHelper的代码如下:
using System;
using System.Collections.Generic;
using System.Reflection;
namespace XFramework.Event
{
public class MessageListenerAttribute : Attribute
{
public string eventName;
public MessageListenerAttribute(string eventName)
{
this.eventName = eventName;
}
}
/// <summary>
/// 消息自动化注册机
/// </summary>
public class RegersterHelper
{
private readonly Dictionary<string, Delegate> m_delegates = new Dictionary<string, Delegate>();
public RegersterHelper(object listener)
{
InitEvent(listener);
}
private void InitEvent(object listener)
{
var methods = listener.GetType().GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var method in methods)
{
var attr = method.GetCustomAttribute<MessageListenerAttribute>();
if (attr != null)
{
var parms = method.GetParameters();
var count = parms.Length;
Type actionType = null;
if (count == 0)
{
actionType = typeof(Action);
}
else if (count == 1)
{
actionType = typeof(Action<>).MakeGenericType(parms[0].ParameterType);
}
else if (count == 2)
{
actionType = typeof(Action<,>).MakeGenericType(parms[0].ParameterType, parms[1].ParameterType);
}
else if (count == 3)
{
actionType = typeof(Action<,,>).MakeGenericType(parms[0].ParameterType, parms[1].ParameterType, parms[2].ParameterType);
}
else
{
throw new Exception("[Messenge] do not spport more than 3 parameter,please redesign listener");
}
Delegate @delegate;
if(method.IsStatic)
@delegate = Delegate.CreateDelegate(actionType, method);
else
@delegate = Delegate.CreateDelegate(actionType, listener, method);
m_delegates.Add(attr.eventName, @delegate);
}
}
}
/// <summary>
/// 注册
/// </summary>
public void Register()
{
foreach (var item in m_delegates)
{
MessageManager.Instance.AddListener(item.Key, item.Value);
}
}
/// <summary>
/// 反注册
/// </summary>
public void UnRegister()
{
foreach (var item in m_delegates)
{
MessageManager.Instance.RemoveListener(item.Key, item.Value);
}
}
/// <summary>
/// 创建一个自动化注册机
/// </summary>
/// <param name="listener">监听者</param>
/// <returns></returns>
public static RegersterHelper Create(object listener)
{
return new RegersterHelper(listener);
}
}
}
3.使用方式
写一个最简单的UI面板基类
public class UIBase
{
private RegersterHelper regersterHelper;
public virtual void OnInit()
{
regersterHelper = RegersterHelper.Create(this);
}
public virtual void OnOpen()
{
regersterHelper.Register();
}
public virtual void OnClose()
{
regersterHelper.UnRegister();
}
[MessageListener("Value1Change")]
private void OnVaule1Change() { }
[MessageListener("Value2Change")]
private void OnVaule2Change(int a) { }
[MessageListener("Value3Change")]
private void OnVaule3Change(int a, string b) { }
[MessageListener("Value4Change")]
private void OnVaule4Change(int a, string b, float c) { }
}
实际项目中,可以在一些经常需要注册消息的基类中封装好RegersterHelper。可以减少大量的注册/反注册代码,也避免了忘记反注册的问题。