C#特性 詳細(xì)介紹

1、什么是Atrribute ******首先,我們肯定Attribute是一個(gè)類,下面是msdn文檔對它的描述:公共語言運(yùn)行時(shí)允許你添加類似關(guān)鍵字的描述聲明,叫做attributes, 它對程序中的元素進(jìn)行標(biāo)注,如類型、字段、方法和屬性等。Attributes和Microsoft .NET Framework文件的元數(shù)據(jù)保存在一起,可以用來向運(yùn)行時(shí)描述你的代碼,或者在程序運(yùn)行的時(shí)候影響應(yīng)用程序的行為。
在.NET中,Attribute被用來處理多種問題,比如序列化、程序的安全特征、防止即時(shí)編譯器對程序代碼進(jìn)行優(yōu)化從而代碼容易調(diào)試等等。下面,我們先來看幾個(gè)在.NET中標(biāo)準(zhǔn)的屬性的使用,稍后我們再回過頭來討論Attribute這個(gè)類本身。(文中的代碼使用C#編寫,但同樣適用所有基于.NET的所有語言)
2、Attribute作為編譯器的指令**在C#中存在著一定數(shù)量的編譯器指令,如:#define DEBUG, #undefine DEBUG, #if等。這些指令專屬于C#,而且在數(shù)量上是固定的。而Attribute用作編譯器指令則不受數(shù)量限制。比如下面的三個(gè)Attribute:
Conditional:起條件編譯的作用,只有滿足條件,才允許編譯器對它的代碼進(jìn)行編譯。一般在程序調(diào)試的時(shí)候使用。 DllImport:用來標(biāo)記非.NET的函數(shù),表明該方法在一個(gè)外部的DLL中定義。 Obsolete:這個(gè)屬性用來標(biāo)記當(dāng)前的方法已經(jīng)被廢棄,不再使用了。下面的代碼演示了上述三個(gè)屬性的使用:

1#define DEBUG //這里定義條件

3using System;
4using System.Runtime.InteropServices;
5using System.Diagnostics;

7namespace AttributeDemo
8{
class MainProgramClass
{

 [DllImport("User32.dll")]
 public static extern int MessageBox(int hParent, string Message, string Caption, int Type);

 static void Main(string[] args)
 {
    DisplayRunningMessage();
    DisplayDebugMessage();

    MessageBox(0,"Hello","Message",0);

    Console.ReadLine();
 }

 [Conditional("DEBUG")]
 private static void DisplayRunningMessage()
 {
    Console.WriteLine("開始運(yùn)行Main子程序。當(dāng)前時(shí)間是"+DateTime.Now);
 }

 [Conditional("DEBUG")]
 //[Obsolete("Don't use Old method, use New method", true)] 
 [Obsolete]
 private static void DisplayDebugMessage()
 {
    Console.WriteLine("開始Main子程序");
 }

}

如果在一個(gè)程序元素前面聲明一個(gè)Attribute,那么就表示這個(gè)Attribute被施加到該元素上,前面的代碼,[DllImport]施加到MessageBox函數(shù)上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。
根據(jù)上面涉及到的三個(gè)Attribute的說明,我們可以猜到程序運(yùn)行的時(shí)候產(chǎn)生的輸出:DllImport Attribute表明了MessageBox是User32.DLL中的函數(shù),這樣我們就可以像內(nèi)部方法一樣調(diào)用這個(gè)函數(shù)。
重要的一點(diǎn)就是Attribute就是一個(gè)類,所以DllImport也是一個(gè)類,Attribute類是在編譯的時(shí)候被實(shí)例化的,而不是像通常的類那樣在運(yùn)行時(shí)候才實(shí)例化。Attribute實(shí)例化的時(shí)候根據(jù)該Attribute類的設(shè)計(jì)可以帶參數(shù),也可以不帶參數(shù),比如DllImport就帶有"User32.dll"的參數(shù)。Conditional對滿足參數(shù)的定義條件的代碼進(jìn)行編譯,如果沒有定義DEBUG,那么該方法將不被編譯,讀者可以把#define DEBUG一行注釋掉看看輸出的結(jié)果(release版本,在Debug版本中Conditional的debug總是成立的)。Obsolete表明了DispalyDebugMessage方法已經(jīng)過時(shí)了,它有一個(gè)更好的方法來代替它,當(dāng)我們的程序調(diào)用一個(gè)聲明了Obsolete的方法時(shí),那么編譯器會給出信息,Obsolete還有其他兩個(gè)重載的版本。大家可以參考msdn中關(guān)于的ObsoleteAttribute 類的描述。
3、Attribute類**除了.NET提供的那些Attribute派生類之外,我們可以自定義我們自己的Attribute,所有自定義的Attribute必須從Attribute類派生?,F(xiàn)在我們來看一下Attribute 類的細(xì)節(jié):
protected Attribute(): 保護(hù)的構(gòu)造器,只能被Attribute的派生類調(diào)用。
三個(gè)靜態(tài)方法:
static Attribute GetCustomAttribute():這個(gè)方法有8種重載的版本,它被用來取出施加在類成員上指定類型的Attribute。
static Attribute[] GetCustomAttributes(): 這個(gè)方法有16種重載版本,用來取出施加在類成員上指定類型的Attribute數(shù)組。
static bool IsDefined():由八種重載版本,看是否指定類型的定制attribute被施加到類的成員上面。
實(shí)例方法:
bool IsDefaultAttribute(): 如果Attribute的值是默認(rèn)的值,那么返回true。
bool Match():表明這個(gè)Attribute實(shí)例是否等于一個(gè)指定的對象。
公共屬性: TypeId: 得到一個(gè)唯一的標(biāo)識,這個(gè)標(biāo)識被用來區(qū)分同一個(gè)Attribute的不同實(shí)例。
我們簡單地介紹了Attribute類的方法和屬性,還有一些是從object繼承來的。這里就不列出來了。
下面介紹如何自定義一個(gè)Attribute: 自定義一個(gè)Attribute并不需要特別的知識,其實(shí)就和編寫一個(gè)類差不多。自定義的Attribute必須直接或者間接地從Attribute這個(gè)類派生,如:
public MyCustomAttribute : Attribute { ... }
這里需要指出的是Attribute的命名規(guī)范,也就是你的Attribute的類名+"Attribute",當(dāng)你的Attribute施加到一個(gè)程序的元素上的時(shí)候,編譯器先查找你的Attribute的定義,如果沒有找到,那么它就會查找“Attribute名稱"+Attribute的定義。如果都沒有找到,那么編譯器就報(bào)錯(cuò)。

4、定義或控制特性的使用**
對于一個(gè)自定義的Attribute,你可以通過AttributeUsage的Attribute來限定你的Attribute 所施加的元素的類型。代碼形式如下:******[AttriubteUsage(參數(shù)設(shè)置)] public 自定義Attribute : Attribute { ... }**
非常有意思的是,AttributeUsage本身也是一個(gè)Attribute,這是專門施加在Attribute類的Attribute. AttributeUsage自然也是從Attribute派生,它有一個(gè)帶參數(shù)的構(gòu)造器,這個(gè)參數(shù)是AttributeTargets的枚舉類型。下面是AttributeTargets 的定義:
1

public enum AttributeTargets 2
{ 3
All=16383, 4
Assembly=1, 5
Module=2, 6
Class=4, 7
Struct=8, 8
Enum=16, 9
Constructor=32,10
Method=64,11
Property=128,12
Field=256,13
Event=512,14
Interface=1024,15
Parameter=2048,16
Delegate=4096,17
ReturnValue=819218
}
public enum AttributeTargets 2
{ 3
All=16383, 4
Assembly=1, 5
Module=2, 6
Class=4, 7
Struct=8, 8
Enum=16, 9
Constructor=32,10
Method=64,11
Property=128,12
Field=256,13
Event=512,14
Interface=1024,15
Parameter=2048,16
Delegate=4096,17
ReturnValue=819218
}
作為參數(shù)的AttributeTarges的值允許通過“或”操作來進(jìn)行多個(gè)值得組合,如果你沒有指定參數(shù),那么默認(rèn)參數(shù)就是All 。 AttributeUsage除了繼承Attribute 的方法和屬性之外,還定義了以下三個(gè)屬性:
AllowMultiple: 讀取或者設(shè)置這個(gè)屬性,表示是否可以對一個(gè)程序元素施加多個(gè)Attribute 。
Inherited:讀取或者設(shè)置這個(gè)屬性,表示是否施加的Attribute 可以被派生類繼承或者重載。
ValidOn: 讀取或者設(shè)置這個(gè)屬性,指明Attribute 可以被施加的元素的類型。
下面讓我們來做一些實(shí)際的東西。我們將會在Help特性前放置AttributeUsage特性以期待在它的幫助下控制Help特性的使用。
復(fù)制代碼

1
using System; 2
[AttributeUsage(AttributeTargets.Class), AllowMultiple = false, 3
Inherited = false ] 4
public class HelpAttribute : Attribute 5
{ 6
public HelpAttribute(String Description_in) 7
{ 8
this.description = Description_in; 9
} 10
protected String description; 11
public String Description 12
{ 13
get 14
{ 15
return this.description; 16
} 17
} 18
}
復(fù)制代碼

先讓我們來看一下AttributeTargets.Class。它規(guī)定了Help特性只能被放在class的前面。這也就意味著下面的代碼將會產(chǎn)生錯(cuò)誤:


復(fù)制代碼

1
[Help("this is a do-nothing class")] 2
public class AnyClass 3

{ 4
[Help("this is a do-nothing method")] //error 5
public void AnyMethod() 6
{ 7
} 8
}
復(fù)制代碼

編譯器報(bào)告錯(cuò)誤如下:
  AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
  It is valid on 'class' declarations only.

我們可以使用AttributeTargets.All來允許Help特性被放置在任何程序?qū)嶓w前??赡艿闹凳牵?br>   Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,Parameter,Delegate。
  All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate,
  ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface )
  下面考慮一下AllowMultiple = false。它規(guī)定了特性不能被重復(fù)放置多次。


復(fù)制代碼

1
[Help("this is a do-nothing class")] 2
[Help("it contains a do-nothing method")] 3
public class AnyClass 4
{ 5
[Help("this is a do-nothing method")] //error 6
public void AnyMethod() 7

{ 8
} 9
}
復(fù)制代碼

它產(chǎn)生了一個(gè)編譯期錯(cuò)誤。
  AnyClass.cs: Duplicate 'Help' attribute
  Ok,現(xiàn)在我們來討論一下最后的這個(gè)屬性。Inherited, 表明當(dāng)特性被放置在一個(gè)基類上時(shí),它能否被派生類所繼承。


復(fù)制代碼

1
[Help("BaseClass")] 2
public class Base 3
{ 4
} 5

6
public class Derive : Base 7
{ 8
}9
復(fù)制代碼

這里會有四種可能的組合:
  [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ]
  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ]
  [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ]
  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ]
  第一種情況:
  如果我們查詢(Query)(稍后我們會看到如何在運(yùn)行期查詢一個(gè)類的特性)Derive類,我們將會發(fā)現(xiàn)Help特性并不存在,因?yàn)閕nherited屬性被設(shè)置為false。
  第二種情況:
  和第一種情況相同,因?yàn)閕nherited也被設(shè)置為false。
  第三種情況:
  為了解釋第三種和第四種情況,我們先來給派生類添加點(diǎn)代碼:


復(fù)制代碼

1
[Help("BaseClass")] 2
public class Base 3
{ 4
} 5
[Help("DeriveClass")] 6
public class Derive : Base 7
{ 8
}
復(fù)制代碼

現(xiàn)在我們來查詢一下Help特性,我們只能得到派生類的屬性,因?yàn)閕nherited被設(shè)置為true,但是AllowMultiple卻被設(shè)置為false。因此基類的Help特性被派生類Help特性覆蓋了。
  第四種情況:
  在這里,我們將會發(fā)現(xiàn)派生類既有基類的Help特性,也有自己的Help特性,因?yàn)锳llowMultiple被設(shè)置為true。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容