AttributeHelper

AttributeHelper

在撰寫類別時,會希望在成員中可以多指定一些額外的資訊,以便在需要的時候取用。

這時候Attribute是個很好用的東西。屬性 (Attribute) 簡介

但因為大部分的類別成員都可以定義屬性(如欄位、Property、方法、建構子等…)

每次取屬性時都要用反射寫一些重複的Code,久而久之實在有點厭煩。

因此最近寫了一套通用取得屬性的Helper,方便以後來使用。

 

Note:在取Property的Attribute時很簡單,但為了有強型別可以使用,所以在這個Helper中

多增加一個多載是傳入Lambda,藉此可以用強型別的方式指定屬性名稱

 

使用方法:


	//通常都會針對同一個Attribute做操作,因此這邊設計成可以先設定一個Attribute
	//再用Get來對不同對象來取得
	//如果只需要取一次,也可直接寫成
	//AttributeHelper.SetAttribute<DescriptionAttribute>().Get<TModel>(...);
	var descriptionAttr=AttributeHelper.SetAttribute<DescriptionAttribute>();
	//類別
    descriptionAttr.Get<Test>();
	//屬性
	descriptionAttr.Get<Test>(p=>p.Name);
	//欄位
	descriptionAttr.Get<Test>(p=>p.file);
	//建構子1
	descriptionAttr.Get<Test>(p=>new Test());
	//建構子2
	descriptionAttr.Get<Test>(p=>new Test(""));
	//方法1
	descriptionAttr.Get<Test>("GetString");
	//方法2
	descriptionAttr.Get<Test>("GetString",typeof(string));
	//事件
	descriptionAttr.Get<Test>("MyDelegate");
	//委派
	descriptionAttr.Get<Test>("MyEvent");
	//列舉1
	descriptionAttr.Get<付款方式>(p=>付款方式.信用卡);
	//列舉2
	付款方式 e=付款方式.現金;
	descriptionAttr.Get<付款方式>(e.ToString());

結果:

2011-07-12_120433

 

ExpressionHelper  解析Expression,取回名稱及參數的Type


    public class ExpressionHelper
    {
        /// <summary> 解析Expression,取回Member名稱及Parameter Type </summary>
        public static ExpressionMemberInfo GetMemberName(Expression exp)
        {
            switch (exp.NodeType)
            {
                case ExpressionType.Convert:
                case ExpressionType.TypeAs:
                    return GetMemberName(((UnaryExpression)exp).Operand);
                case ExpressionType.Constant:
                    return GetExpressionMemberInfo(((ConstantExpression)exp).Value.ToString());
                case ExpressionType.MemberAccess:
                        return GetExpressionMemberInfo(((MemberExpression)exp).Member.Name);
                case ExpressionType.Call:
                    return VisitMethod((MethodCallExpression)exp);
                case ExpressionType.Lambda:
                    return GetMemberName(((LambdaExpression)exp).Body);
                case ExpressionType.New:
                    return VisitNew((NewExpression)exp);
                default:
                    throw new NotSupportedException(String.Format("未處理的Expression : '{0}'", exp.NodeType));
            }
        }

        private static ExpressionMemberInfo GetExpressionMemberInfo(string memberName)
        {
            return new ExpressionMemberInfo() { MemberName = memberName };
        }

        /// <summary> 解析MethodCallExpression </summary>
        protected static ExpressionMemberInfo VisitMethod(MethodCallExpression methodExp)
        {
            return new ExpressionMemberInfo() { 
                               MemberName = methodExp.Method.Name, 
                               ParameterTypes = methodExp.Arguments.Select(p => p.Type) };
        }

        /// <summary> 解析NewExpression </summary>
        protected static ExpressionMemberInfo VisitNew(NewExpression newExp)
        {
            return new ExpressionMemberInfo() { 
                                 MemberName = ".ctor", 
				 ParameterTypes = newExp.Arguments.Select(p => p.Type) };
        }

        /// <summary> 解析完成後,回傳的類別(包含Member名稱跟Type的陣列) </summary>
        public class ExpressionMemberInfo
        {
            public string MemberName { get; set; }

            public IEnumerable<Type> ParameterTypes { get; set; }
        }
    }

 

AttributeHelper


    public class AttributeHelper
    {
        #region -- 屬性與建構子 --

        protected AttributeHelper()
        {
        }

        protected BindingFlags _BindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;

        /// <summary> 設定BindingFlags </summary>
        public BindingFlags SettingBindingFlags { get { return _BindingFlags; } set { _BindingFlags = value; } }

        /// <summary> 要擷取的Attribute </summary>
        protected Type AttributeType { get; set; }

        #endregion

        #region -- 方法 --

        /// <summary> 設定要擷取的Attribute </summary>
        public static AttributeHelper<T> SetAttribute<T>()
        {
            var helper = new AttributeHelper<T>();
            helper.AttributeType = typeof(T);
            return helper;
        }

        protected object GetMemberAttributes(Type type)
        {
            return type.GetCustomAttributes(false).FirstOrDefault();
        }

        protected object GetMemberAttributes(Type type, ExpressionHelper.ExpressionMemberInfo expressionMemberInfo)
        {
            MemberInfo memberInfo = null;
            if (expressionMemberInfo.ParameterTypes == null)
            {
                memberInfo = type.GetMember(expressionMemberInfo.MemberName, _BindingFlags).FirstOrDefault();
            }
            else
            {
                memberInfo = type.GetMember(expressionMemberInfo.MemberName, _BindingFlags)
                                 .FirstOrDefault(p => ((MethodBase)p).GetParameters()
                                 .Select(s => s.ParameterType)
                                 .SequenceEqual(expressionMemberInfo.ParameterTypes)
                                 );
            }
            return GetResult(memberInfo);
        }

        private object GetResult(MemberInfo memberInfo)
        {
            if (memberInfo == null) throw new ArgumentNullException();

            var attr = memberInfo.GetCustomAttributes(AttributeType, false).FirstOrDefault();
            if (memberInfo == null) return null;

            return attr;
        }

        #endregion

        #region -- 快速使用區(如果有常用的Attribute,可以擴充在這邊) --

        public static DescriptionAttribute GetDescription<TModel>()
        {
            return AttributeHelper.SetAttribute<DescriptionAttribute>().Get<TModel>();
        }

        public static DescriptionAttribute GetDescription<TModel>(string memberName, params Type[] types)
        {
            return AttributeHelper.SetAttribute<DescriptionAttribute>().Get<TModel>(memberName, types);
        }

        public static DescriptionAttribute GetDescription<TModel>(Expression<Func<TModel, object>> func)
        {
            return AttributeHelper.SetAttribute<DescriptionAttribute>().Get<TModel>(func);
        }

        #endregion
    }

    public class AttributeHelper<T> : AttributeHelper
    {
        protected internal AttributeHelper()
        {
        }

        /// <summary> 取得類別的Attribute </summary>
        public T Get<TModel>()
        {
            return (T)GetMemberAttributes(typeof(TModel));
        }

        /// <summary>
        /// 取得指定成員名稱的Attribute
        /// </summary>
        /// <typeparam name="TModel">目標型別</typeparam>
        /// <param name="memberName">成員名稱</param>
        /// <param name="types">如果是MethodBase,可傳入參數的Type</param>
        public T Get<TModel>(string memberName, params Type[] types)
        {
            return (T)GetMemberAttributes(typeof(TModel), new ExpressionHelper.ExpressionMemberInfo() { MemberName = memberName, ParameterTypes=types.ToList() });
        }

        /// <summary>
        /// 取得指定成員名稱的Attribute
        /// </summary>
        /// <typeparam name="TModel">目標型別</typeparam>
        /// <param name="func">可用Lambda的方式指定成員名稱</param>
        public T Get<TModel>(Expression<Func<TModel, object>> func)
        {
            var expressionMemberInfo = ExpressionHelper.GetMemberName(func);
            return (T)GetMemberAttributes(typeof(TModel), expressionMemberInfo);
        }
    }

 

 

 

 

測試類別:


    [Description("測試類別")]
    public class Test
    {
        [Description("建構子1")]
        public Test()
        {
        }

        [Description("建構子2")]
        public Test(string name)
        {
        }

        [Description("姓名")]
        public string Name { get; set; }

        [Description("年齡")]
        public string Age;

        [Description("方法1")]
        public void GetString()
        {
        }

        [Description("方法2")]
        public void GetString(string name)
        {
        }

        [Description("欄位")]
        public string file = "";

        [Description("委派")]
        public delegate void MyDelegate();

        [Description("事件")]
        public event MyDelegate MyEvent;

    }

    public enum 付款方式
    {
        [Description("CreditCard")]
        信用卡,
        [Description("Cash")]
        現金
    }

目前測試大部分的情況都可以正常運作。

附上範例Code,如果有問題的話可以一起研究研究