第十一节:特性(常见的特性标签、自定义特性、特性的使用案例)
一. 基本概念
1. 什么是特性?
MSDN官方給出的定義時:公共語言運行時允許添加類似關(guān)鍵字的描述聲明,叫做特性,它對程序中的元素進行標注,如類型、字段、方法和屬性等。Attribute和Microsoft .Net Framework文件的元數(shù)據(jù)(metadata)保存在一起,可以用來向運行時描述你的代碼,或者在程序運行時影響程序的行為。?
? ??我的理解:在不影響類封裝的情況的下,額外的添加一些信息,如果你用這個信息,則特性有效;如果你不用這個信息,那么這個特性無效。我們通常使用反射的方式獲取類、屬性、或方法等上面標注的特性。
2. 分清三個概念
? ?(1). 注釋:寫在代碼上面,一般用“ // ”和“ /**/ ”兩個符號來表示,用來說明代碼段或代碼塊的含義,方便自己或別人理解,對代碼運行沒有任何影響。
? ?(2). 屬性:屬性和特性雖然一字之差,但是完全兩個不同的概念,屬性在面向?qū)ο笾?#xff0c;提供了私有或字段的封裝,可以通過get和set訪問器來設(shè)置可讀可寫。
? ?(3). 特性:不影響類的封裝,在運行時,可以通過反射獲取特性的內(nèi)容。
3. DotNet中常用特性
? ?(1).?[Serializable]和[NonSerialized] :表示可以序列化或不可以序列化。
? ?(2).?[Obsolete("該類不能用了",true)]:表示該類(或?qū)傩曰蜃侄?#xff09;將不能被使用。
? ?(3).?[AttributeUsage]:用來限制特性的作用范圍、是否允許多次修飾同一個對象、是否允許被繼承。
? ?(4). [ReadOnly(true)]:?表示該特性作用于的屬性為只讀屬性。
? ?(5).?[Description("XXX")]:用來描述作用對象含義的。
? ?(6).?[Flags]:?指示可以將枚舉作為位域(即一組標志)處理
? ?(7).?[DllImport("")] ??: 使用包含要導(dǎo)入的方法的 DLL 的名稱初始化
二. 自定義特性
1. 可作用的范圍?
???程序集(assembly)、模塊(module)、類型(type)、屬性(property)、事件(event)、字段(field)、方法(method)、參數(shù)(param)、返回值(return)。
2. 約定規(guī)則?
? (1).?聲明以Attribute結(jié)尾的類,即xxx+Attribute。
? (2).?必須繼承或間接繼承Attribute類。
? (3). 必須要有公有的構(gòu)造函數(shù)。
3. 特性的使用方式(eg: ypfAttribute特性 ?mrAttribute特性)
? (1). 可以省略后綴,也可以不省略。 ?eg:[ypfAttribute]、[ypf]、[ypfAttribute()]、[ypf()] ?。
? (2). 多個特性共同作用于一個對象的使用方式:?[ypfAttribute][mrAttribute]、?[ypfAttribute,mrAttribute] ?(注:也可以省略后綴的各種組合形式)
4. 特性的構(gòu)建
? (1). 特性中除了可以聲明構(gòu)造函數(shù),還可以聲明屬性和字段。(方法是不可以的)
? (2). 可以通過DotNet自帶的特性來限制自定義的特性。?[AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =false)]
a:AttributeTargets.All 表示可以加在所有的上面(包括類、屬性、接口),也可以根據(jù)自己的需求,比如 AttributeTargets.Class 只允許加在類上。
b:約束該特性能否同時作用于某個元素(類、方法、屬性等等)多次,默認為false。
c: ?約束該特性作用于基類(或其它)上,其子類能否繼承該特性,默認為true。
?5. 下面自定義一個ypf特性
1 [AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =false)]2 public class ypfAttribute : Attribute3 {4 /// <summary>5 /// 默認的構(gòu)造函數(shù)6 /// </summary>7 public ypfAttribute()8 {9 10 } 11 12 /// <summary> 13 /// 新聲明的構(gòu)造函數(shù) 14 /// </summary> 15 public ypfAttribute(int id) 16 { 17 18 } 19 /// <summary> 20 /// 新聲明的構(gòu)造函數(shù) 21 /// </summary> 22 public ypfAttribute(int id,string name) 23 { 24 25 } 26 /// <summary> 27 /// 聲明要給屬性 28 /// </summary> 29 public string Remark { get; set; } 30 31 /// <summary> 32 /// 聲明一個字段 33 /// </summary> 34 public string Description = null; 35 }?(1). ?作用形式
1 /// <summary>2 /// 用戶實體類3 /// </summary>5 [ypfAttribute]6 [ypf]7 [ypfAttribute()]8 [ypf()] //以上四個等價9 [ypf(123)] 10 [ypfAttribute(123)] 11 [ypf(123, "456")] 12 [ypfAttribute(123, "456")] 13 [ypf(Remark = "這里是特性")] 14 [ypf(123, Remark = "這里是特性")] 15 [ypfAttribute(123, Remark = "這里是特性")] 16 [ypf(123, "456", Remark = "這里是特性")] 17 [ypfAttribute(123, "456", Remark = "這里是特性", Description = "Description")] 18 19 [Table("User")] 20 public class UserModel 21 { 22 /// <summary> 23 /// 主鍵ID 24 /// </summary> 25 [myValidate(1, 1000)] 26 public int Id { get; set; } 27 /// <summary> 28 /// 賬號 29 /// </summary> 30 public string Account { get; set; } 31 /// <summary> 32 /// 密碼 33 /// </summary> 34 public string Password { get; set; } 35 /// <summary> 36 /// EMaill 37 /// </summary> 38 public string Email { get; set; } 39 40 }(2). 如何獲取特性中值??(???[ypfAttribute(123, "456", Remark = "這里是特性", Description = "Description")]??)
A. 重新構(gòu)建ypfAttribute中的內(nèi)容,需要在該特性內(nèi)部封裝四個獲取四個內(nèi)容的方法 1 [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]2 public class ypfAttribute : Attribute3 {4 public int _id;5 public string _name;6 7 /// <summary>8 /// 默認的構(gòu)造函數(shù)9 /// </summary> 10 public ypfAttribute() 11 { 12 13 } 14 15 /// <summary> 16 /// 新聲明的構(gòu)造函數(shù) 17 /// </summary> 18 public ypfAttribute(int id) 19 { 20 21 } 22 /// <summary> 23 /// 新聲明的構(gòu)造函數(shù) 24 /// </summary> 25 public ypfAttribute(int id, string name) 26 { 27 this._id = id; 28 this._name = name; 29 } 30 /// <summary> 31 /// 聲明要給屬性 32 /// </summary> 33 public string Remark { get; set; } 34 35 /// <summary> 36 /// 聲明一個字段 37 /// </summary> 38 public string Description = null; 39 40 //下面封裝四個方法,分別獲取該屬性中的內(nèi)容 41 public int GetOwnId() 42 { 43 return this._id; 44 } 45 public string GetOwnName() 46 { 47 return this._name; 48 } 49 public string GetOwnRemark() 50 { 51 return this.Remark; 52 } 53 public string GetOwnDescription() 54 { 55 return this.Description; 56 } 57 }B:封裝獲取特性內(nèi)容的可供外部調(diào)用的方法(在該方法中要調(diào)用ypf內(nèi)部的封裝的方法)
? 1 /// <summary>2 /// 根據(jù)類型獲取自定義特性ypfAttribute中的四個內(nèi)容3 /// </summary>4 /// <typeparam name="T"></typeparam>5 /// <param name="t"></param>6 public static void GetypfAttributeMsg<T>(this T t) where T : new()7 {8 //1.獲取類9 Type type = t.GetType(); 10 //2. 獲取類中的所有特性 11 object[] attributeList = type.GetCustomAttributes(true); 12 //3. 查找ypf特性 13 foreach (var item in attributeList) 14 { 15 if (item is ypfAttribute) 16 { 17 ypfAttribute ypfattr = item as ypfAttribute; 18 int id = ypfattr.GetOwnId(); 19 string Name = ypfattr.GetOwnName(); 20 string remark = ypfattr.GetOwnRemark(); 21 string Des = ypfattr.GetOwnDescription(); 22 Console.WriteLine("ypfAttribute上的四個內(nèi)容分別為:{0},{1},{2},{3}",id,Name,remark,Des); 23 } 24 } 25 } C. 調(diào)用 1 { 2 //測試獲取UserModel類上的ypfAttribute中的四個內(nèi)容 3 Console.WriteLine("----------------------測試獲取UserModel類上的ypfAttribute中的四個內(nèi)容--------------------"); 4 UserModel userModel = new UserModel(); 5 userModel.GetypfAttributeMsg(); 6 }D. 結(jié)果
三. 案例(制作一個驗證屬性長度的特性)
1. 構(gòu)建一個myValidateAttribute特性,里面包含校驗方法。
1 /// <summary>2 /// 驗證int類型屬性長度的特性3 /// </summary>4 [AttributeUsage(AttributeTargets.Property)] //表示該特性只能作用于屬性上5 public class myValidateAttribute:Attribute6 {7 private int _min = 0;8 private int _max = 0;9 10 /// <summary> 11 /// 自定義構(gòu)造函數(shù) 12 /// </summary> 13 /// <param name="min"></param> 14 /// <param name="max"></param> 15 public myValidateAttribute(int min,int max) 16 { 17 this._min = min; 18 this._max = max; 19 } 20 /// <summary> 21 /// 封裝校驗是否合法的方法 22 /// </summary> 23 /// <param name="num"></param> 24 /// <returns></returns> 25 public bool CheckIsRational(int num) 26 { 27 return num >= this._min && num <= this._max; 28 } 29 }2. 外部校驗方法
1 /// <summary>2 /// 校驗并保存的方法3 /// </summary>4 /// <typeparam name="T"></typeparam>5 /// <param name="t"></param>6 public static void Save<T>(T t)7 {8 bool isSafe = true;9 //1. 獲取實例t所在的類 10 Type type = t.GetType(); 11 //2. type.GetProperties() 獲取類中的所有屬性 12 foreach (var property in type.GetProperties()) 13 { 14 //3. 獲取該屬性上的所有特性 15 object[] attributesList = property.GetCustomAttributes(true); 16 //4. 找屬性中的特性 17 foreach (var item in attributesList) 18 { 19 if (item is myValidateAttribute) 20 { 21 myValidateAttribute attribute = item as myValidateAttribute; 22 //調(diào)用特性中的校驗方法 23 //表示獲取屬性的值:property.GetValue(t) 24 isSafe = attribute.CheckIsRational((int)property.GetValue(t)); 25 } 26 } 27 if (!isSafe) 28 { 29 break; 30 } 31 } 32 if (isSafe) 33 { 34 Console.WriteLine("保存到數(shù)據(jù)庫"); 35 } 36 else 37 { 38 Console.WriteLine("數(shù)據(jù)不合法"); 39 } 40 }3. 調(diào)用
1 { 2 //制作一個可以限制int類型屬性長度的特性,并封裝對應(yīng)的校驗方法 3 UserModel userModel = new UserModel(); 4 // userModel.Id = 1000; 5 userModel.Id = 1001; // 不合法 6 BaseDal.Save<UserModel>(userModel); 7 }4. 結(jié)果
四. 總結(jié)
??自定義特性的使用步驟: 聲明特性→特性中封裝獲取特性參數(shù)的方法→將特性作用于對象上→封裝外部校驗作用對象的方法→調(diào)用
??封裝外部校驗作用對象的方法要用到反射,這里簡單補充一下反射在知識:
? ?反射詳見章節(jié): ? ?.Net進階系列(2)-反射
總結(jié)
以上是生活随笔為你收集整理的第十一节:特性(常见的特性标签、自定义特性、特性的使用案例)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: rsync算法原理及使用
- 下一篇: 爆款预订!iPhone 14 Pro“紫