C# 特性(attribute)
一、什么是特性
特性是一種允許我們向程序的程序集添加元數(shù)據(jù)的語言結(jié)構(gòu),它是用于保存程序結(jié)構(gòu)信息的某種特殊類型的類。
MSDN中對(duì)它的解釋是:特性提供功能強(qiáng)大的方法以將聲明信息與 C# 代碼(類型、方法、屬性等)相關(guān)聯(lián)。特性與程序?qū)嶓w關(guān)聯(lián)后,即可在運(yùn)行時(shí)使用名為“反射”的技術(shù)查詢屬性。
(有關(guān)元數(shù)據(jù)和反射的知識(shí),點(diǎn)擊查看 C# 反射)
?
二、使用特性
特性的目的是告訴編譯器把程序結(jié)構(gòu)的某組元數(shù)據(jù)嵌入程序集,它可以放置在幾乎所有的聲明中(但特定的屬性可能限制在其上有效的聲明類型)。其語法為:
● 在結(jié)構(gòu)前放置特性片段來運(yùn)用特性
● 特性片段被方括號(hào)包圍,其中是特性名和特性的參數(shù)列表
例:
[Serializable] //不含參數(shù)的特性public class MyClass{...}[MyAttribute("firt","second","finally")] //帶有參數(shù)的特性 public class MyClass {...}注: 大多數(shù)特性只針對(duì)直接跟隨在一個(gè)或多個(gè)特性片段后的結(jié)構(gòu)
?
單個(gè)結(jié)構(gòu)可以運(yùn)用多個(gè)特性,使用時(shí)可以把獨(dú)立的特性片段互相疊在一起或使用分成單個(gè)特性片段,特性之間用逗號(hào)分隔
[Serializable] [MyAttribute("firt","second","finally")] //獨(dú)立的特性片段 ...[MyAttribute("firt","second","finally"), Serializable] //逗號(hào)分隔
...
?
某些屬性對(duì)于給定實(shí)體可以指定多次。例如,Conditional?就是一個(gè)可多次使用的屬性:
[Conditional("DEBUG"), Conditional("TEST1")] void TraceMethod() {// ... }?
?特性的目標(biāo)是應(yīng)用該特性的實(shí)體。例如,特性可以應(yīng)用于類、特定方法或整個(gè)程序集。默認(rèn)情況下,特性應(yīng)用于它后面的元素。但是,您也可以顯式標(biāo)識(shí)要將特性應(yīng)用于方法還是它的參數(shù)或返回值。
| C#中標(biāo)準(zhǔn)特性目標(biāo)名稱 | 適用對(duì)象 |
| assembly | 整個(gè)程序集 |
| module | 當(dāng)前程序集模塊(不同于Visual Basic 模塊) |
| field | 在類或結(jié)構(gòu)中的字段 |
| event | Event |
| method | 方法或get和set屬性訪問器 |
| param | 方法參數(shù)或set屬性訪問器的參數(shù) |
| property | Property |
| return | 方法、屬性索引器或get屬性訪問器的返回值 |
| type | 結(jié)構(gòu)、類、接口、枚舉或委托 |
| typevar | 指定使用泛型結(jié)構(gòu)的類型參數(shù) |
?
三、自定義特性
特性的用法雖然很特殊,但它只是某個(gè)特殊類型的類。
?
3.1 聲明自定義的特性
總體上聲明特性和聲明其他類是一樣的,只是所有的特性都派生自System.Attribute。根據(jù)慣例,特性名使用Pascal命名法并且以Attribute后綴結(jié)尾,當(dāng)為目標(biāo)應(yīng)用特性時(shí),我們可以不使用后綴。如:對(duì)于SerializableAttribute
和MyAttributeAttribute這兩個(gè)特性,我們?cè)诎阉鼞?yīng)用到結(jié)構(gòu)的時(shí)候可以使用[Serializable和MyAttribute短名
public class MyAttributeAttribute : System.Attribute {...}當(dāng)然它也有構(gòu)造函數(shù)。和其他類一樣,每個(gè)特性至少有一個(gè)公共構(gòu)造函數(shù),如果你不聲明構(gòu)造函數(shù),編譯器會(huì)產(chǎn)生一個(gè)隱式、公共且無參的構(gòu)造函數(shù)。
public class MyAttributeAttribute : System.Attribute{public string Description;public string ver;public string Reviwer;public MyAttributeAttribute(string desc,string ver,string Rev) //構(gòu)造函數(shù) {Description = desc;this.ver = ver;Reviwer = Rev;}}?
3.2? 限制特性的使用
前面我們已經(jīng)知道,可以在類上面運(yùn)用特性,而特性本身就是類,有一個(gè)很重要的預(yù)定義特性AttributeUsage可以運(yùn)用到自定義特性上,我們可以用它來限制特性使用在某個(gè)目標(biāo)類型上
下面的例子使自定義的特性只能應(yīng)用到方法和類上
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]public class MyAttributeAttribute : System.Attribute{...}簡(jiǎn)單解讀一下AttributeUsage特性,它有三個(gè)重要的公共屬性,如下表
| 名字 | 意義 | 默認(rèn)值 |
| ValidOn | 指定特性允許的目標(biāo)類型。構(gòu)造函數(shù)的第一個(gè)參數(shù)必須是AttributeTarget類型的枚舉值 | ? |
| Inherited | 布爾值,指示特性能否被派生類和重寫成員繼承 | ?true |
| AllowMultiple | 布爾值,指示特性能否被重復(fù)放置在同一個(gè)程序?qū)嶓w前多次 | ?false |
在vs中按f12查閱定義我們可以看到,AttributeTarget枚舉的成員有
看一個(gè)小例子
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, //必須的,指示MyAttribute只能應(yīng)用到類和方法上Inherited = false, //可選,表明不能被派生類繼承AllowMultiple = false)] //可選,表明不能有MyAttribute的多個(gè)實(shí)例應(yīng)用到同一個(gè)目標(biāo)上public class MyAttributeAttribute : System.Attribute{...}?
3.3訪問特性
定義好特性了,怎么進(jìn)行訪問呢?對(duì)于自定義的特性,我們可以用Type中的IsDefined和GetCustomAttributes方法來獲取
?
3.3.1 使用IsDefined方法
public abstract bool IsDefined(Type attributeType, bool inherit),它是用來檢測(cè)某個(gè)特性是否應(yīng)用到了某個(gè)類上
參數(shù)說明:? ? attributeType :?要搜索的自定義特性的類型。 搜索范圍包括派生的類型。
inherit:true 搜索此成員繼承鏈,以查找這些屬性;否則為 false。 屬性和事件,則忽略此參數(shù)
返回結(jié)果:?true 如果一個(gè)或多個(gè)實(shí)例 attributeType 或其派生任何的類型為應(yīng)用于此成員; 否則為 false。
? 下面代碼片段是用來檢查MyAttribute特性是否被運(yùn)用到MyClass類
MyClass mc = new MyClass(); Type t = mc.GetType(); bool def = t.IsDefined(typeof(MyAttributeAttribute),false); if (def)Console.WriteLine("MyAttribute is defined!");?
? 3.3.2 使用GetCustomAttributes方法
? public abstract object[] GetCustomAttributes(bool inherit),調(diào)用它后,會(huì)創(chuàng)建每一個(gè)與目標(biāo)相關(guān)聯(lián)的特性的實(shí)例
參數(shù)說明: inherit:?true 搜索此成員繼承鏈,以查找這些屬性;否則為 false
返回結(jié)果:返回所有應(yīng)用于此成員的自定義特性的數(shù)組,因此我們必須將它強(qiáng)制轉(zhuǎn)換為相應(yīng)的特性類型
?
//自定義特性 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class MyAttributeAttribute : System.Attribute{public string Description;public string ver;public string Reviwer;public MyAttributeAttribute(string desc,string ver,string Rev){Description = desc;this.ver = ver;Reviwer = Rev;}}//定義類 [MyAttribute("firt","second","finally")]class MyClass{}static void Main(string[] args){MyClass mc = new MyClass();Type t = mc.GetType();Object[] obj = t.GetCustomAttributes(false);foreach(Attribute a in obj){MyAttributeAttribute attr = a as MyAttributeAttribute;if(attr != null){Console.WriteLine("Description : {0}", attr.Description);Console.WriteLine("ver : {0}", attr.ver);Console.WriteLine("review: {0}", attr.Reviwer);}}}結(jié)果:
?
四、預(yù)定義的特性
4.1 Obsolete特性
Obsolete特性將public ObsoleteAttribute()程序結(jié)構(gòu)標(biāo)注為過期的,并且在代碼編譯時(shí)顯式有用的警告信息,它有三種重載
public ObsoleteAttribute()
public ObsoleteAttribute(string message) 參數(shù)說明: message:描述了可選的變通方法文本字符串。
public ObsoleteAttribute(string message, bool error) 參數(shù)說明:message:描述了可選的變通方法文本字符串。 error:true 如果使用過時(shí)的元素將生成編譯器錯(cuò)誤; false 如果使用它將生成編譯器警告。
舉個(gè)例子:
using System; using System.Runtime.CompilerServices;namespace 特性 {class Program{[Obsolete("Use method SuperPrintOut")]static void Print(string str,[CallerFilePath] string filePath = ""){Console.WriteLine(str);Console.WriteLine("filePath {0}", filePath);}static void Main(string[] args){string path = "no path";Print("nothing",path);Console.ReadKey();}} }運(yùn)行沒有問題,不過出現(xiàn)了警告:
如果將?[Obsolete("Use method SuperPrintOut")]? 改成[Obsolete("Use method SuperPrintOut",true)] 的話,編譯則會(huì)出現(xiàn)錯(cuò)誤信息
?
?
? 4.2 Conditional 特性
public ConditionalAttribute(string conditionString),指示編譯器,如果定義了conditionString編譯符號(hào),就和普通方法沒有區(qū)別,否則忽略代碼中方法這個(gè)方法的所有調(diào)用
#define fun //定義編譯符號(hào) using System; using System.Runtime.CompilerServices;namespace 特性 {class Program{[Conditional("fun")]static void Fun(string str){Console.WriteLine(str);}static void Main(string[] args){Fun("hello");Console.ReadKey();}}}由于定義了fun,所以Fun函數(shù)會(huì)被調(diào)用,如果沒有定義,這忽略Fun函數(shù)的調(diào)用
?
4.3 調(diào)用者信息特性
調(diào)用者信息特性可以訪問文件路徑、代碼行數(shù)、調(diào)用成員的名稱等源代碼信息,這三個(gè)特性的名稱分別為CallerFilePath、CallerLineNumber和CallerMemberName,這些方法只能用于方法中的可選參數(shù)
using System; using System.Runtime.CompilerServices;namespace 特性 {class Program{static void Print(string str,[CallerFilePath] string filePath = "",[CallerLineNumber] int num = 0,[CallerMemberName] string name = ""){Console.WriteLine(str);Console.WriteLine("filePath {0}", filePath);Console.WriteLine("Line {0}", num);Console.WriteLine("Call from {0}", name);}static void Main(string[] args){Print("nothing");Console.ReadKey();}} }?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/forever-Ys/p/10428568.html
總結(jié)
以上是生活随笔為你收集整理的C# 特性(attribute)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么使用消息队列? 消息队列有什么优点
- 下一篇: 一只身披黑甲,头部有一对触角、三对足、两