C#進階之特性(Attribute)


相信只要是用過.net MVC的開發都用過特性,例如最基礎的HttpMethodAttribute特性,用來給接口進行請求方式限定的,等等諸如此類的特性,數不勝數。

那么什么是特性?

特性就是一個類,直接或者間接繼承自Attribute,特性本身沒有任何作用

特性的使用方法有兩種:

        [Range(0,10)]
        [Test]
        ///分開單獨使用
        public int Hight { set; get; }
        [Test, Range(0, 10)]
        ///合並使用,通過逗號分隔
        public string Name { set; get; }

attribute的使用規則,一般通過在特性類上使用AttributeUsage特性來進行設置

這個特性有三個屬性:

AllowMultiple:是否允許重復使用;

Inherited:是否可以被繼承;

構造函數有AttributeTargets類型的參數,主要用來設置特性的使用場景(類、接口、屬性、字段……)

 

 

特性的使用場景:

用來做標記,通過判斷類、屬性……是否有這個特性,來進行一些業務邏輯上的判斷,比如:

具體是通過System.Reflection程序集提供的反射功能來進行特性獲取,

譬如判斷一個類型是否具有某個特性,使用的是IsDefined方法,

獲取一個類的所有特性,使用的是GetCustomAttributes方法,獲取的是一個object[],可以通過as進行強轉為需要判斷的類型;

1、一些預設的特性

ObsoleteAttribute:可以通過添加這個特性,對類、屬性、接口等進行代碼過期設置;

ConditionalAttribute:通過在方法上設置這個特性,可以屏蔽掉對該方法的所有調用(不過支持返回值是void的方法,畢竟只有這樣才不會有在調用的時候有上下文);

經常看到錯誤日志里面有很詳細的信息,具體到了某某文件多少行哪個方法出錯等等,其實微軟也提供了對應的特性,支持我們自己查看這些內容:

/// <summary>
        /// 
        /// </summary>
        /// <param name="filePath">調用的文件物理路徑</param>
        /// <param name="num">被調用的行</param>
        /// <param name="name">調用的方法</param>
        public void Call2(string str,
            [CallerFilePath] string filePath = "",
            [CallerLineNumber] int num = 0,
            [CallerMemberName] string name = "")
        {
        }

2、接口的模型綁定屬性驗證

 可以通過繼承ValidationAttribute,並重寫IsValid方法即可實現自定義的屬性綁定校驗。

/// <summary>
    ///  
    /// </summary>
    [AttributeUsage(AttributeTargets.All,AllowMultiple = true,Inherited = true)]
    public class RangeAttribute : ValidationAttribute
    {
        public RangeAttribute(int minNum, int maxNum)
        {
            MaxNum = maxNum;
            MinNum = minNum;
        }

        public int? MaxNum { set; get; }
        public int? MinNum { set; get; }
        public override bool IsValid(object value)
        {
            var result = false;
            if (MaxNum != null)
                result = (int)value < MaxNum;
            if (MinNum != null)
                result = result && (int)value > MinNum;
            return result;
        }
    }

3、可以用來做屬性的標記

舉個例子,比如我想要對一個列表進行多個字段的檢索,比如這個實體

/// <summary>
    /// 全局搜索查詢demo實體
    /// </summary>
    public class SerachEntity
    {
        /// <summary>
        /// 姓名
        /// </summary>
        [GlobalSerach]
        [Export("姓名")]
        public string Name { set; get; }
        /// <summary>
        /// 中文名
        /// </summary>
        [GlobalSerach]
        [Export("中文名")]
        public string CName { set; get; }
        /// <summary>
        /// 英文名
        /// </summary>
        [GlobalSerach]
        [Export("英文名")]
        public string EName { set; get; }
        /// <summary>
        /// 地址
        /// </summary>
        [GlobalSerach]
        [Export("地址")]
        public string Address { set; get; }
        /// <summary>
        /// IP
        /// </summary>
        [Export("IP地址")]
        public string IP { set; get; }
    }
    /// <summary>
    /// 全局搜索的標記特性
    /// </summary>
    public class GlobalSerachAttribute : Attribute
    { 
        
    }

我想要對這些屬性進行一個綜合的查詢

那么不可能來一個需求我就寫一個where吧,這樣並不符合封裝的思想,所以我需要做的就是尋找共同點,進行封裝

public static class BaseSerach 
    {
        private static List<BinaryExpression> exps = new List<BinaryExpression>();
        private static ParameterExpression m_Parameter = null;
        /// <summary>
        /// where全局查找語句
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="iEnumerables"></param>
        /// <param name="str"></param>
        /// <returns></returns>
        public static IEnumerable<T> WhereByGlobalSerach<T>(this IEnumerable<T> iEnumerables, string str)
            where T : class
        {
            m_Parameter = Expression.Parameter(typeof(T), "x");
            var propertys = typeof(T).GetProperties().Where(t => !(t.IsDefined(typeof(SerializableAttribute), true))).ToList();
            propertys.ForEach(t=> {
                MemberExpression member = Expression.PropertyOrField(m_Parameter, t.Name);
                Expression exp1 = Expression.NotEqual(member, Expression.Constant(null));//判斷是否為null
                Expression exp2 = Expression.Call(member, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(str));
                exps.Add(Expression.AndAlso(exp1, exp2));
            });
            return iEnumerables.Where(GetLambda<T>());

        }
        /// <summary>
        /// 獲取表達式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private static Func<T, bool> GetLambda<T>()
            where T : class
        {
            Expression whereExpr = null;
            foreach (var expr in exps)
            {
                if (whereExpr == null)
                    whereExpr = expr;
                else
                    whereExpr = Expression.Or(whereExpr, expr);
            }
            if (whereExpr == null)
                return null;
            return Expression.Lambda<Func<T, bool>>(whereExpr, m_Parameter).Compile();
        }
    }

這里的方法就可以用來進行泛型的普遍屬性檢索,

調用起來也很簡單

            var beforeList = new List<SerachEntity>() { new SerachEntity() {Name="余成" },new SerachEntity() {Name="余承浩" } };
            var lastList = beforeList.WhereByGlobalSerach("");

我感覺這就是特性最好用的一點了,用於標記。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM