我是靠谱客的博主 闪闪御姐,最近开发中收集的这篇文章主要介绍Attribute实用详解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

老实讲,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实用详解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部