一、什么是特性
特性(Attribute)是用於在運行時傳遞程序中各種元素(比如類、方法、結構、枚舉、組件等)的行為信息的聲明性標簽。您可以通過使用特性向程序添加聲明性信息。一個聲明性標簽是通過放置在它所應用的元素前面的方括號([ ])來描述的。
特性(Attribute)用於添加元數據,如編譯器指令和注釋、描述、方法、類等其他信息。.Net 框架提供了兩種類型的特性:預定義特性和自定義特性。
特性的語法如下:
[attribute(positional_parameters, name_parameter = value, ...)]
element
特性(Attribute)的名稱和值是在方括號內規定的,放置在它所應用的元素之前。positional_parameters 規定必需的信息,name_parameter 規定可選的信息。
二、預定義特性
1、Obsolete特性
這個預定義特性標記了不應被使用的程序實體。它可以讓您通知編譯器丟棄某個特定的目標元素。例如,當一個新方法被用在一個類中,但是您仍然想要保持類中的舊方法,您可以通過顯示一個應該使用新方法,而不是舊方法的消息,來把它標記為 obsolete(過時的)。
語法如下:
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
其中:
- 參數 message,是一個字符串,描述項目為什么過時的原因以及該替代使用什么。
- 參數 iserror,是一個布爾值。如果該值為 true,編譯器應把該項目的使用當作一個錯誤。默認值是 false(編譯器生成一個警告)。
請看下面的一個小例子:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute 8 { 9 [Obsolete("請不要使用該類了,該類已經過時了,請使用什么代替")] 10 public class Student 11 { 12 public int Id { get; set; } 13 public string Name { get; set; } 14 15 public string Accont { get; set; } 16 17 public long QQ { get; set; } 18 19 public string Answer([Custom]string name) 20 { 21 return $"This is {name}"; 22 } 23 } 24 }
上面的例子中,在Student類上面使用了Obsolete特性來標注該類已經過時了。編譯代碼結果:
三、自定義特性
.Net 框架允許創建自定義特性,用於存儲聲明性的信息,且可在運行時被檢索。該信息根據設計標准和應用程序需要,可與任何目標元素相關。
創建並使用自定義特性包含四個步驟:
- 聲明自定義特性
- 構建自定義特性
- 在目標程序元素上應用自定義特性
- 通過反射訪問特性
1、聲明自定義特性
在上面的例子中,使用F12查看Obsolete的定義:
從上面的截圖中可以看出,.NET框架中的預定義特性是繼承自Attribute類,所以要自定義一個特性,只需要該類繼承自Attribute即可,下面定義一個Custom自定義特性:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute 8 { 9 /// <summary> 10 /// 自定義Custom特性 11 /// </summary> 12 public class CustomAttribute :Attribute 13 { 14 15 } 16 }
注意:所有的特性默認以Attribute結尾,但聲明的時候可以不以Attribute結尾。
2、構建自定義特性
每個特性必須至少有一個構造函數。必需的定位( positional)參數應通過構造函數傳遞。下面的代碼演示了CustomAttribute類:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute 8 { 9 /// <summary> 10 /// 自定義Custom特性 11 /// </summary> 12 public class CustomAttribute :Attribute 13 { 14 /// <summary> 15 /// 無參構造函數 16 /// </summary> 17 public CustomAttribute() 18 { 19 20 } 21 22 /// <summary> 23 /// 有參構造函數 24 /// </summary> 25 /// <param name="id"></param> 26 public CustomAttribute(string description) 27 { 28 this.Description = description; 29 } 30 31 /// <summary> 32 /// 屬性 33 /// </summary> 34 public string Description { get; set; } 35 36 /// <summary> 37 /// 字段 38 /// </summary> 39 public string Remark = null; 40 41 public void Show() 42 { 43 Console.WriteLine("This Is CustomAttribute"); 44 } 45 } 46 }
3、在目標程序元素上應用自定義特性
通過把特性放置在緊接着它的目標(類、方法、屬性、字段等)上面,來應用該特性:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute 8 { 9 [Obsolete("請不要使用該類了,該類已經過時了")] 10 [Custom("這是Custom自定義特性")] 11 public class Student 12 { 13 public int Id { get; set; } 14 public string Name { get; set; } 15 16 public string Accont { get; set; } 17 18 public long QQ { get; set; } 19 20 public string Answer([Custom]string name) 21 { 22 return $"This is {name}"; 23 } 24 } 25 }
注意:
1、如果在聲明自定義特性的時候使用了Attribute結尾,那么應用自定義特性的時候可以把Attribute省略掉;如果聲明的時候沒有以Attribute結尾,那么應用自定義特性的時候就不能把Attribute省略掉。
2、默認情況下相同的特性只能應用一次,如果想應用多次特性,那么需要給特性添加AttributeUsage特性,CustomAttribute特性修改如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute 8 { 9 /// <summary> 10 /// 自定義Custom特性 11 /// </summary> 12 [AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =true)] 13 public class CustomAttribute :Attribute 14 { 15 /// <summary> 16 /// 無參構造函數 17 /// </summary> 18 public CustomAttribute() 19 { 20 21 } 22 23 /// <summary> 24 /// 有參構造函數 25 /// </summary> 26 /// <param name="id"></param> 27 public CustomAttribute(string description) 28 { 29 this.Description = description; 30 } 31 32 /// <summary> 33 /// 屬性 34 /// </summary> 35 public string Description { get; set; } 36 37 /// <summary> 38 /// 字段 39 /// </summary> 40 public string Remark = null; 41 42 public void Show() 43 { 44 Console.WriteLine("This Is CustomAttribute"); 45 } 46 } 47 }
其中,AttributeTargets是枚舉值,F12轉到定義可以查看AttributeTargets的所有枚舉值:
AttributeTargets的枚舉值表示Custom特性可以應用在哪些目標上面。例如:AttributeTargets的枚舉值是Class,則表示CustomAttribute只能應用在類上面。這里枚舉值是All,表示可以在任何類型上面使用該特性。默認情況下枚舉值是All。
AllowMultiple表示該特性是否可以在類型上面多次使用:
這里AllowMultiple的值為true,表示可以在類型上面多次使用該特性。如果為false,則表示只能使用一次。默認情況下是false。
Inherited表示該特性是否可以由子類繼承:
默認情況下Inherited為true。
這是在看Student類:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute 8 { 9 [Obsolete("請不要使用該類了,該類已經過時了")] 10 [Custom("這是Custom自定義特性")]//使用有參構造 11 [Custom()]//使用無參構造 12 public class Student 13 { 14 public int Id { get; set; } 15 16 /// <summary> 17 /// 在屬性上面使用Custom特性 18 /// </summary> 19 [Custom("這是Name屬性")] 20 public string Name { get; set; } 21 22 public string Accont { get; set; } 23 24 public long QQ { get; set; } 25 26 /// <summary> 27 /// 在方法和參數上面使用Custom特性 28 /// </summary> 29 /// <param name="name"></param> 30 /// <returns></returns> 31 [Custom("這是Answer方法")] 32 public string Answer([Custom("這是方法參數")]string name) 33 { 34 return $"This is {name}"; 35 } 36 } 37 }
注意:如果一個類型上面多次使用了同一種特性,那么特性可以寫在一起,中間用逗號隔開,例如上面的定義和下面的是同樣的效果:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute 8 { 9 [Obsolete("請不要使用該類了,該類已經過時了")] 10 [Custom("這是Custom自定義特性"),Custom,Custom(),Custom(Remark ="備注")] 11 12 public class Student 13 { 14 public int Id { get; set; } 15 16 /// <summary> 17 /// 在屬性上面使用Custom特性 18 /// </summary> 19 [Custom("這是Name屬性")] 20 public string Name { get; set; } 21 22 public string Accont { get; set; } 23 24 public long QQ { get; set; } 25 26 /// <summary> 27 /// 在方法、方法參數、方法的返回值上面使用Custom特性 28 /// </summary> 29 /// <param name="name"></param> 30 /// <returns></returns> 31 [Custom("這是Answer方法")]//方法上面應用特性 32 [return:Custom()] //方法的返回值應用特性 33 public string Answer([Custom("這是方法參數")]string name) 34 { 35 return $"This is {name}"; 36 } 37 } 38 }
注意:在Web API中FromBaby和FromUri就是給方法的參數應用特性。
4、通過反射訪問特性
定義一個Manager類來管理特性:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Reflection; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace MyAttribute 9 { 10 /// <summary> 11 /// 管理特性 12 /// </summary> 13 public class Manager 14 { 15 public static void Show(Student student) 16 { 17 // 獲取類型 18 Type type = typeof(Student); //或者使用student.GetType(); 19 // 找到類型上面的特性 type.IsDefined表示找類型上面的特性 20 if (type.IsDefined(typeof(CustomAttribute), true))//檢查有沒有 性能高 21 { 22 //GetCustomAttribute 獲取特性 type.GetCustomAttribute表示找到類型上面定義的特性,表示調用構造函數創建一個CustomAttribute類型的對象 23 CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true); 24 // attribute.Description表示特性類里面的屬性 attribute.Remark表示特性類里面的字段 25 Console.WriteLine($"{attribute.Description}_{attribute.Remark}"); 26 attribute.Show(); 27 } 28 29 30 #region 獲取ID屬性上面定義的特性 31 // 獲取Id屬性 32 PropertyInfo property = type.GetProperty("Id"); 33 //檢查Id屬性上面是否定義了CustomAttribute特性 34 if (property.IsDefined(typeof(CustomAttribute), true)) 35 { 36 CustomAttribute attribute = (CustomAttribute)property.GetCustomAttribute(typeof(CustomAttribute), true); 37 Console.WriteLine($"{attribute.Description}_{attribute.Remark}"); 38 attribute.Show(); 39 } 40 #endregion 41 42 #region 獲取Answer()方法上面定義的特性 43 // 獲取Answer方法 44 MethodInfo method = type.GetMethod("Answer"); 45 if (method.IsDefined(typeof(CustomAttribute), true)) 46 { 47 CustomAttribute attribute = (CustomAttribute)method.GetCustomAttribute(typeof(CustomAttribute), true); 48 Console.WriteLine($"{attribute.Description}_{attribute.Remark}"); 49 attribute.Show(); 50 } 51 #endregion 52 53 #region 獲取參數定義的特性 54 ParameterInfo parameter = method.GetParameters()[0]; 55 if (parameter.IsDefined(typeof(CustomAttribute), true)) 56 { 57 CustomAttribute attribute = (CustomAttribute)parameter.GetCustomAttribute(typeof(CustomAttribute), true); 58 Console.WriteLine($"{attribute.Description}_{attribute.Remark}"); 59 attribute.Show(); 60 } 61 #endregion 62 63 #region 獲取返回值定義的特性 64 ParameterInfo returnParameter = method.ReturnParameter; 65 if (returnParameter.IsDefined(typeof(CustomAttribute), true)) 66 { 67 CustomAttribute attribute = (CustomAttribute)returnParameter.GetCustomAttribute(typeof(CustomAttribute), true); 68 Console.WriteLine($"{attribute.Description}_{attribute.Remark}"); 69 attribute.Show(); 70 } 71 #endregion 72 73 string result = student.Answer("Tom"); 74 Console.WriteLine(result); 75 } 76 } 77 }
Main()方法里面調用:
1 using MyAttribute.Extension; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace MyAttribute 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 Student student = new Student(); 15 student.Id = 123; 16 student.Name = "time"; 17 // 使用Manager類管理Student 18 Manager.Show(student); 19 20 Console.ReadKey(); 21 } 22 } 23 }
結果:
四、應用特性
場景一:用戶狀態的枚舉值,定義的是英文的字段,需要輸出中文含義。枚舉定義如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute.Extension 8 { 9 /// <summary> 10 /// 枚舉類型 用戶狀態 11 /// </summary> 12 public enum UserState 13 { 14 /// <summary> 15 /// 正常 16 /// </summary> 17 Normal = 0, 18 /// <summary> 19 /// 凍結 20 /// </summary> 21 Frozen = 1, 22 23 /// <summary> 24 /// 刪除 25 /// </summary> 26 Deleted = 2 27 } 28 }
普通做法:根據枚舉值進行判斷,然后輸出中文含義:
1 UserState userState = UserState.Normal; 2 switch(userState) 3 { 4 case UserState.Normal: 5 Console.WriteLine("正常"); 6 break; 7 case UserState.Frozen: 8 Console.WriteLine("凍結"); 9 break; 10 case UserState.Deleted: 11 Console.WriteLine("刪除"); 12 break; 13 }
這種寫法違反開不原則,不利於以后的擴展,下面使用特性實現。
先定義Remark特性:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Reflection; 7 8 namespace MyAttribute.Extension 9 { 10 /// <summary> 11 /// RemarkAttribute 特性 12 /// </summary> 13 public class RemarkAttribute :Attribute 14 { 15 private string _Remark = null; 16 /// <summary> 17 /// 有參構造 18 /// </summary> 19 /// <param name="remark"></param> 20 public RemarkAttribute(string remark) 21 { 22 this._Remark = remark; 23 } 24 25 public string GetRemark() 26 { 27 return _Remark; 28 } 29 } 30 }
UserState枚舉修改如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute.Extension 8 { 9 /// <summary> 10 /// 枚舉類型 用戶狀態 11 /// </summary> 12 public enum UserState 13 { 14 /// <summary> 15 /// 正常 16 /// </summary> 17 [Remark("正常")] 18 Normal = 0, 19 /// <summary> 20 /// 凍結 21 /// </summary> 22 [Remark("凍結")] 23 Frozen = 1, 24 25 /// <summary> 26 /// 刪除 27 /// </summary> 28 [Remark("刪除")] 29 Deleted = 2 30 } 31 }
對Enum類型進行擴展:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Reflection; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace MyAttribute.Extension 9 { 10 public static class EnumExtension 11 { 12 /// <summary> 13 /// Enum的擴展方法,靜態類、靜態方法 第一個參數前面添加this關鍵字 14 /// </summary> 15 /// <param name="value"></param> 16 /// <returns></returns> 17 public static string GetRemark(this Enum value) 18 { 19 // 獲取類型 20 Type type = value.GetType(); 21 // 獲取字段 22 FieldInfo field = type.GetField(value.ToString()); 23 // 判斷字段上面是否定義了RemarkAttribute特性 24 if (field.IsDefined(typeof(RemarkAttribute))) 25 { 26 // 創建實例 27 RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute)); 28 // 返回RemarkAttribute特性里面的GetRemark()方法 29 return attribute.GetRemark(); 30 } 31 else 32 { 33 return value.ToString(); 34 } 35 } 36 } 37 }
Main()方法里面調用:
1 using MyAttribute.Extension; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace MyAttribute 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 Student student = new Student(); 15 student.Id = 123; 16 student.Name = "time"; 17 // 使用Manager類管理Student 18 //Manager.Show(student); 19 20 21 UserState userState = UserState.Normal; 22 //switch(userState) 23 //{ 24 // case UserState.Normal: 25 // Console.WriteLine("正常"); 26 // break; 27 // case UserState.Frozen: 28 // Console.WriteLine("凍結"); 29 // break; 30 // case UserState.Deleted: 31 // Console.WriteLine("刪除"); 32 // break; 33 //} 34 Console.WriteLine(userState.GetRemark()); 35 Console.ReadKey(); 36 } 37 } 38 }
結果:
二、做數據校驗
Student中有QQ這個屬性,范圍是10000-999999999999,校驗QQ屬性的值在這個范圍區間內。
1、定義一個RangeAttribute特性,用來驗證屬性范圍
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute.Extension 8 { 9 /// <summary> 10 /// 定義LongAttribute特性,並且特性只能應用在字段和屬性上面 11 /// </summary> 12 [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)] 13 public class RangeAttribute :Attribute 14 { 15 /// <summary> 16 /// 最小范圍 17 /// </summary> 18 private long _MinRange = 0; 19 20 /// <summary> 21 /// 最大范圍 22 /// </summary> 23 private long _MaxRange = 0; 24 25 public RangeAttribute(long min,long max) 26 { 27 this._MinRange = min; 28 this._MaxRange = max; 29 } 30 31 /// <summary> 32 /// 檢查屬性范圍 33 /// </summary> 34 /// <param name="value"></param> 35 /// <returns></returns> 36 public bool Check(object value) 37 { 38 if(value!=null && !string.IsNullOrWhiteSpace(value.ToString())) 39 { 40 if(long.TryParse(value.ToString(),out long IResult)) 41 { 42 if(IResult>this._MinRange && IResult<this._MaxRange) 43 { 44 return true; 45 } 46 } 47 } 48 49 return false; 50 } 51 } 52 }
2、在Student類的QQ屬性上面應用特性
1 [Range(10001,999999999999)] 2 public long QQ { get; set; }
3、對Object類型進行擴展
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Reflection; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace MyAttribute.Extension 9 { 10 /// <summary> 11 /// object類型的驗證擴展 12 /// </summary> 13 public static class ObjectExtension 14 { 15 /// <summary> 16 /// 對object類型擴展一個Validate的方法 17 /// </summary> 18 /// <param name="obj"></param> 19 /// <param name="msg">輸出參數,輸出驗證信息。如果驗證通過,輸出空字符串;如果驗證不通過,輸出具體信息</param> 20 /// <returns></returns> 21 public static bool Validate(this object obj,out string msg) 22 { 23 // 獲取類型 24 Type type = obj.GetType(); 25 // 獲取屬性 26 PropertyInfo[] propertyInfos= type.GetProperties(); 27 foreach(PropertyInfo prop in propertyInfos) 28 { 29 if(prop.IsDefined(typeof(LongAttribute))) 30 { 31 LongAttribute attribute = (LongAttribute)prop.GetCustomAttribute(typeof(LongAttribute)); 32 if(!attribute.Check(prop.GetValue(obj))) 33 { 34 msg = prop.Name + "檢查失敗"; 35 return false; 36 } 37 } 38 } 39 msg = ""; 40 return true; 41 42 } 43 } 44 }
4、在Manager類里面使用Validate擴展方法
// 驗證 string msg = string.Empty; bool tfResult= student.Validate(out msg); if(!tfResult) { Console.WriteLine(msg); }
5、在Main()方法里面調用
1 Student student = new Student(); 2 student.Id = 123; 3 student.Name = "time"; 4 student.QQ = 9999; 5 // 使用Manager類管理Student 6 Manager.Show(student);
結果:
如果這時候Student里面增加了Name屬性,並且要驗證Name屬性的長度,這時需要增加一個驗證屬性長度的特性:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute.Extension 8 { 9 /// <summary> 10 /// 驗證長度的特性,只能應用於字段和屬性上面 11 /// </summary> 12 [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)] 13 public class LengthAttribute:Attribute 14 { 15 /// <summary> 16 /// 最小長度 17 /// </summary> 18 private int _MinLength = 0; 19 20 /// <summary> 21 /// 最大長度 22 /// </summary> 23 private int _MaxLength = 0; 24 25 public LengthAttribute(int min, int max) 26 { 27 this._MinLength = min; 28 this._MaxLength = max; 29 } 30 31 /// <summary> 32 /// 檢查屬性長度 33 /// </summary> 34 /// <param name="value"></param> 35 /// <returns></returns> 36 public bool Check(object value) 37 { 38 if (value != null && !string.IsNullOrWhiteSpace(value.ToString())) 39 { 40 if (long.TryParse(value.ToString(), out long IResult)) 41 { 42 if (IResult > this._MinLength && IResult < this._MaxLength) 43 { 44 return true; 45 } 46 } 47 } 48 49 return false; 50 } 51 } 52 }
在Student類的Name屬性上面應用LengthAttribute特性:
1 [Length(5,10)] 2 public string Name { get; set; }
在ObjectExtension里面增加長度的驗證:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Reflection; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace MyAttribute.Extension 9 { 10 /// <summary> 11 /// object類型的驗證擴展 12 /// </summary> 13 public static class ObjectExtension 14 { 15 /// <summary> 16 /// 對object類型擴展一個Validate的方法 17 /// </summary> 18 /// <param name="obj"></param> 19 /// <param name="msg">輸出參數,輸出驗證信息。如果驗證通過,輸出空字符串;如果驗證不通過,輸出具體信息</param> 20 /// <returns></returns> 21 public static bool Validate(this object obj,out string msg) 22 { 23 // 獲取類型 24 Type type = obj.GetType(); 25 // 獲取屬性 26 PropertyInfo[] propertyInfos= type.GetProperties(); 27 foreach(PropertyInfo prop in propertyInfos) 28 { 29 // 檢查屬性上面是否定義了RangeAttribute特性 30 if (prop.IsDefined(typeof(RangeAttribute))) 31 { 32 RangeAttribute attribute = (RangeAttribute)prop.GetCustomAttribute(typeof(RangeAttribute)); 33 if(!attribute.Check(prop.GetValue(obj))) 34 { 35 msg = string.Format($"屬性{ prop.Name}范圍檢查失敗"); 36 return false; 37 } 38 } 39 40 // 檢查屬性上面是否定義了LengthAttribute特性 41 if (prop.IsDefined(typeof(LengthAttribute))) 42 { 43 LengthAttribute attribute = (LengthAttribute)prop.GetCustomAttribute(typeof(LengthAttribute)); 44 if (!attribute.Check(prop.GetValue(obj))) 45 { 46 msg = string.Format($"屬性{ prop.Name}長度檢查失敗"); 47 return false; 48 } 49 } 50 } 51 msg = ""; 52 return true; 53 54 } 55 } 56 }
最后在Main()方法里面調用:
1 Student student = new Student(); 2 student.Id = 123; 3 student.Name = "time"; 4 // 使用Manager類管理Student 5 Manager.Show(student);
結果:
仔細查看ObjectExtension擴展類:每增加一個特性,擴展方法里面就要增加一段相同的代碼(只是特性的類型不同),那么能不能做到增加特性,而這里不需要修改呢?請看下面的修改:
1、定義一個抽象類繼承自Attribute,里面有一個抽象的Check()方法,定義如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute.Extension 8 { 9 /// <summary> 10 /// 抽象基類,繼承自Attribute 11 /// </summary> 12 public abstract class AbstractValidateAttribute:Attribute 13 { 14 public abstract bool Check(object value); 15 } 16 }
2、修改RangeAttribute和LengthAttribute兩個特性類,都繼承自AbstractValidateAttribute基類
RangeAttribute類:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute.Extension 8 { 9 /// <summary> 10 /// 定義LongAttribute特性,並且特性只能應用在字段和屬性上面 11 /// </summary> 12 [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)] 13 public class RangeAttribute : AbstractValidateAttribute 14 { 15 /// <summary> 16 /// 最小范圍 17 /// </summary> 18 private long _MinRange = 0; 19 20 /// <summary> 21 /// 最大范圍 22 /// </summary> 23 private long _MaxRange = 0; 24 25 public RangeAttribute(long min,long max) 26 { 27 this._MinRange = min; 28 this._MaxRange = max; 29 } 30 31 /// <summary> 32 /// 重寫基類方法 檢查屬性范圍 33 /// </summary> 34 /// <param name="value"></param> 35 /// <returns></returns> 36 public override bool Check(object value) 37 { 38 if(value!=null && !string.IsNullOrWhiteSpace(value.ToString())) 39 { 40 if(long.TryParse(value.ToString(),out long IResult)) 41 { 42 if(IResult>this._MinRange && IResult<this._MaxRange) 43 { 44 return true; 45 } 46 } 47 } 48 49 return false; 50 } 51 } 52 }
LengthAttribute類:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MyAttribute.Extension 8 { 9 /// <summary> 10 /// 驗證長度的特性,只能應用於字段和屬性上面 11 /// </summary> 12 [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)] 13 public class LengthAttribute: AbstractValidateAttribute 14 { 15 /// <summary> 16 /// 最小長度 17 /// </summary> 18 private int _MinLength = 0; 19 20 /// <summary> 21 /// 最大長度 22 /// </summary> 23 private int _MaxLength = 0; 24 25 public LengthAttribute(int min, int max) 26 { 27 this._MinLength = min; 28 this._MaxLength = max; 29 } 30 31 /// <summary> 32 /// 重寫基類方法 檢查屬性長度 33 /// </summary> 34 /// <param name="value"></param> 35 /// <returns></returns> 36 public override bool Check(object value) 37 { 38 if (value != null && !string.IsNullOrWhiteSpace(value.ToString())) 39 { 40 if (long.TryParse(value.ToString(), out long IResult)) 41 { 42 if (IResult > this._MinLength && IResult < this._MaxLength) 43 { 44 return true; 45 } 46 } 47 } 48 return false; 49 } 50 } 51 }
3、修改ObjectExtension擴展類
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Reflection; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace MyAttribute.Extension 9 { 10 /// <summary> 11 /// object類型的驗證擴展 12 /// </summary> 13 public static class ObjectExtension 14 { 15 /// <summary> 16 /// 對object類型擴展一個Validate的方法 17 /// </summary> 18 /// <param name="obj"></param> 19 /// <param name="msg">輸出參數,輸出驗證信息。如果驗證通過,輸出空字符串;如果驗證不通過,輸出具體信息</param> 20 /// <returns></returns> 21 public static bool Validate(this object obj,out string msg) 22 { 23 // 獲取類型 24 Type type = obj.GetType(); 25 // 獲取屬性 26 PropertyInfo[] propertyInfos= type.GetProperties(); 27 foreach(PropertyInfo prop in propertyInfos) 28 { 29 // 判斷屬性上面是否定義了AbstractValidateAttribute特性 30 if (prop.IsDefined(typeof(AbstractValidateAttribute),true)) 31 { 32 // 屬性上面可能會定義多個特性,所以這里使用數組 33 object[] attributeArray = prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true); 34 foreach(AbstractValidateAttribute attribute in attributeArray) 35 { 36 if (!attribute.Check(prop.GetValue(obj))) 37 { 38 msg = string.Format($"屬性{ prop.Name}檢查失敗"); 39 return false; 40 } 41 } 42 } 43 44 //// 檢查屬性上面是否定義了RangeAttribute特性 45 //if (prop.IsDefined(typeof(RangeAttribute))) 46 //{ 47 // RangeAttribute attribute = (RangeAttribute)prop.GetCustomAttribute(typeof(RangeAttribute)); 48 // if(!attribute.Check(prop.GetValue(obj))) 49 // { 50 // msg = string.Format($"屬性{ prop.Name}范圍檢查失敗"); 51 // return false; 52 // } 53 //} 54 55 //// 檢查屬性上面是否定義了LengthAttribute特性 56 //if (prop.IsDefined(typeof(LengthAttribute))) 57 //{ 58 // LengthAttribute attribute = (LengthAttribute)prop.GetCustomAttribute(typeof(LengthAttribute)); 59 // if (!attribute.Check(prop.GetValue(obj))) 60 // { 61 // msg = string.Format($"屬性{ prop.Name}長度檢查失敗"); 62 // return false; 63 // } 64 //} 65 } 66 msg = ""; 67 return true; 68 69 } 70 } 71 }
4、運行結果:
經過上面的修改以后,如果以后要新增一個特性,那么該特性只需要在本類中重寫基類的Check()方法即可,而不需要在修改ObjectExtension擴展類。