相信只要是用過.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("余");
我感覺這就是特性最好用的一點了,用於標記。
