特性


一、什么是特性

特性(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擴展類。


免責聲明!

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



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