概述
老实讲,Attribute又是一个很早以前就接触到的东西,但当时看了很多网上讲解仍是一知半解。用了Unity后,看到很多诸如MenuItem、ContextMenuItem的特性,还是没有太过在意。直到之前项目写编辑器,以及最近项目在运行时代码中大量使用Attribute,又细细研究了一番,发觉这玩意儿作用挺大的,值得好好研究。
介于网上有很多讲解文章,这里就不在详述Attribute到底是个什么东西了,主要针对以前看完Attirbute一头雾水不知实际用途,举一些简单例子,方便理解。
1. 网络回调
以前做端游的时候,客户端接收到某个网络消息后,处理方法一般如下:
1.
public enum NetWorkId{
NetWork_Login = 0,
NetWork_Account = 1,
NetWork_CreateRole = 2,
}
2.
public class NetworkManager{
private static Dictionary<NetWorkId, System.Action> m_netHandleMap = new Dictionary<NetWorkId, System.Action>();
public static void AddListener(NetWorkId netId, System.Action handle)
{
m_netHandleMap.Add (netId, handle);
}
public static void CallNetworkHandle(NetWorkId netId)
{
m_netHandleMap [netId] ();
}
}
3.
NetworkManager.AddListener (NetWorkId.NetWork_Login, OnHandleLogin);
代码是随手写的,没有任何异常处理,没有太多细节,理解万岁。
一般是3部分:
1.定义所有消息的id,大多数用enum
2.有个管理类,负责注册函数,以及根据具体消息id,找出处理函数再去调用
3.在需要的地方具体去注册某个消息对应的处理函数
这种方式蛮不美观的,毕竟如上代码,你第一眼看到OnHandleLogin函数肯定不知道是干嘛用的,虽然很多人可能会说写好注释啊。。。
尝试下用Attribute改写代码:
1.
public enum NetWorkId{
NetWork_Login = 0,
NetWork_Account = 1,
NetWork_CreateRole = 2,
}
2.
[AttributeUsage(AttributeTargets.Method)]
public class NetWorkAttribute : Attribute{
public NetWorkId m_id;
public NetWorkAttribute(NetWorkId id)
{
m_id = id;
}
}
3.
public class NetWorkManager{
Dictionary<NetWorkId, MethodInfo> m_netHandleMap = new Dictionary<NetWorkId, MethodInfo>();
public void Init(){
var assemblies = System.AppDomain.CurrentDomain.GetAssemblies ();
foreach (Assembly assembly in assemblies) {
var types = assembly.GetTypes();
foreach(Type t in types){
var methods = t.GetMethods();
foreach(MethodInfo method in methods){
var attributes = method.GetCustomAttributes();
foreach(Attribute attr in attributes){
if(attr is NetWorkAttribute)
m_netHandleMap.Add(attr.m_id, method);
}
}
}
}
}
}
4.
[NetWorkAttribute(NetWorkId.NetWork_Login)]
public void OnHandleLogin()
{
}
怎么感觉步骤还多了一个出来呢?而且第三步中Manager中的代码为什么看起来那么长,那么吓人。。。
别急,第二步定义了一个Attribute是一劳永逸的。定义这个NetworkAttribute到底是干嘛用的呢?就是为了给第四步中的处理函数打标机。打完标记,然后呢?这就需要回到第三步,那么长的代码就是为了让我们程序去自动搜索,搜索出代码中所有被NetworkAttribute标记过的函数,然后把这些函数存储起来。然后接收到某个消息时,调用这些处理函数。
对比第一种方法,有什么优势呢?一是美观,所有被标记的函数,一眼就知道这原来是处理网络消息的函数,而且根据标记参数还能直观知道函数对应的消息id是什么。二,也是更重要的,被标记的函数会被自动搜集起来,减少人为错误。试想一下用第一种方式,你定义了消息id,处理函数,但是忘了使用AddListener函数注册,那么服务器发再多的消息也没用。而且,使用AddListener方式我们必须确保代码可以走到AddListener(XXX)这一步(Init或者某个函数中调用到)。如果我们用Attribute标记函数,我们可以把函数弄成静态函数,这就可以保证函数一定会被注册并且可以被调用。
2.Unity中制作编辑器
同样的,我们可以在Unity编辑器中大量使用定制的Attribute,来完成很多功能。如下代码所示:
首先定义一个Attribute:
public EditorAttribute : Attribute
{
public Type type;
public EditorAttribute(Type t){
type = try;
}
}
接着定义两个功能类:
public class Effect
{
}
public class Animation
{
}
接着就是编辑类:
public class EditorBase
{
public virtual void OnGUI()
{
Console.WriteLine("EditorBase OnGUI");
}
}
[EditorAttribute(typeof(Effect))]
public class EffectEditor : EditorBase
{
public override void OnGUI()
{
Console.WriteLine("EffectEditor OnGUI");
}
}
[EditorAttribute(typeof(Animation))]
public class AnimationEditor : EditorBase
{
public override void OnGUI()
{
Console.WriteLine("AnimationEditor OnGUI");
}
}
用管理类来做搜集和创建工作:
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace MyAttributeTest
{
public class AttributeManager
{
// Key [EditorAttribute()]中传入的Type Value EditorAttribute修饰的类
static Dictionary<Type, Type> m_typeMap = new Dictionary<Type, Type>();
public static void Init()
{
var assembiles = System.AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assemb in assembiles)
{
var types = assemb.GetTypes();
foreach (Type type in types)
{
var attributes = type.GetCustomAttributes();
foreach (Attribute attr in attributes)
{
EditorAttribute editorAttribute = attr as EditorAttribute;
if (editorAttribute != null)
m_typeMap[editorAttribute.type] = type;
}
}
}
}
public static EditorBase CreateEditor(Type t)
{
EditorBase editor = null;
if (m_typeMap[t] != null)
{
Type EditorType = m_typeMap[t];
editor = System.Activator.CreateInstance(EditorType) as EditorBase;
Console.WriteLine("Editor type " + EditorType.Name);
}
return editor;
}
}
}
Init函数会搜集所有被EditorAttribute标记的类,然后存储起来。CreateEditor函数则用传入的类型生成对应的编辑类。调用如下:
Effect effect = new Effect();
Animation animation = new Animation();
EditorBase effectEditor = AttributeManager.CreateEditor(effect.GetType());
EditorBase animationEditor = AttributeManager.CreateEditor(animation.GetType());
effectEditor.OnGUI();
animationEditor.OnGUI();
如此一来,只要传入逻辑类的类型,就可以自动生成对应的编辑类,大大简化了我们的工作量,并且不用每次都为哪一个逻辑类应该生成哪一个编辑类犯愁。而且,如果是引用子类对象的基类变量,也可以依赖类型直接生成对应的子对象编辑类。
关于Attribute的实用例子就讲到这里,如果你有更好的使用方式,请给我留言,不胜感激。
部分代码放到Github,想要的自行提取:https://github.com/sadgeminids/AttributeLearning
最后
以上就是闪闪御姐为你收集整理的Attribute实用详解的全部内容,希望文章能够帮你解决Attribute实用详解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复