屬性和元數據
C#和C++之間一個顯著的區別是它提供了對元數據的支持:有關類、對象、方法等其他實體的數據。屬性可以分為二類:一類以CLR的一部分的形式出現,另一種是我們自己創建的屬性,CLR屬性用來支持串行化、排列和COM協同性等。一些屬性是針對一個組合體的,有些屬性則是針對類或界面,它們也被稱作是屬性目標。
將屬性放在屬性目標前的方括號內,屬性就可以作用于它們的屬性目標。
[assembly:AssemblyDelaySign(false)] [assembly:AssemblyKeyFile(".\\keyFile.snk")]
或用逗號將各個屬性分開:
[assembly:AssemblyDelaySign(false), assembly:AssemblyKeyFile(".\\keyFile.snk")]
自定義的屬性
我們可以任意創建自定義屬性,并在認為合適的時候使用它們。假設我們需要跟蹤bug的修復情況,就需要建立一個包含bug的數據庫,但需要將bug報告與專門的修正情況綁定在一塊兒,則可能在代碼中添加如下所示的注釋:
//Bug323fixedbyJesseLiberty1/1/2005.
這樣,在源代碼中就可以一目了然地了解bug的修正情況,但如果如果把相關的資料保存在數據庫中可能會更好,這樣就更方便我們的查詢工作了。如果所有的bug報告都使用相同的語法那就更好了,但這時我們就需要一個定制的屬性了。我們可能使用下面的內容代替代碼中的注釋:
[BugFix(323,"JesseLiberty","1/1/2005")Comment="Offbyoneerror"]
與C#中的其他元素一樣,屬性也是類。定制化的屬性類需要繼承System.Attribute:
publicclassBugFixAttribute:System.Attribute
我們需要讓編譯器知道這個屬性可以跟什么類型的元素,我們可以通過如下的方式來指定該類型的元素:
[AttributeUsage(AttributeTargets.ClassMembers,AllowMultiple=true)]
AttributeUsage是一個作用于屬性的屬性━━元屬性,它提供的是元數據的元數據,也即有關元數據的數據。在這種情況下,我們需要傳遞二個參數,第一個是目標(在本例中是類成員。),第二個是表示一個給定的元素是否可以接受多于一個屬性的標記。AllowMultiple的值被設置為true,意味著類成員可以有多于一個BugFixAttribute屬性。如果要聯合二個屬性目標,可以使用OR操作符連接它們。
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface,AllowMultiple=true)]
上面的代碼將使一個屬性隸屬于一個類或一個界面。
新的自定義屬性被命名為BugFixAttribute。命名的規則是在屬性名之后添加Attribute。在將屬性指派給一個元素后,編譯器允許我們使用精簡的屬性名調用這一屬性。因此,下面的代碼是合法的:
[BugFix(123,"JesseLiberty","01/01/05",Comment="Offbyone")]
編譯器將首先查找名字為BugFix的屬性,如果沒有發現,則查找BugFixAttribute。
每個屬性必須至少有一個構造器。屬性可以接受二種類型的參數:環境參數和命名參數。在前面的例子中,bugID、編程人員的名字和日期是環境參數,注釋是命名參數。環境參數被傳遞到構造器中的,而且必須按在構造器中定義的順序傳遞。
publicBugFixAttribute(intbugID,stringprogrammer,stringdate) { this.bugID=bugID; this.programmer=programmer; this.date=date; }
Namedparametersareimplementedasproperties.
屬性的使用
為了對屬性進行測試,我們創建一個名字為MyMath的簡單類,并給它添加二個函數,然后給它指定bugfix屬性。
[BugFixAttribute(121,"JesseLiberty","01/03/05")]
[BugFixAttribute(107,"JesseLiberty","01/04/05", Comment="Fixedoffbyoneerrors")] publicclassMyMath
這些數據將與元數據存儲在一起。下面是完整的源代碼及其輸出:
自定義屬性
usingSystem; //創建被指派給類成員的自定義屬性 [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)] publicclassBugFixAttribute:System.Attribute { //位置參數的自定義屬性構造器 publicBugFixAttribute (intbugID, stringprogrammer, stringdate) { this.bugID=bugID; this.programmer=programmer; this.date=date; } publicintBugID { get { returnbugID; } }
//命名參數的屬性 publicstringComment { get { returncomment; } set { comment=value; } }
publicstringDate { get { returndate; } }
publicstringProgrammer { get { returnprogrammer; } }
//專有成員數據 privateintbugID; privatestringcomment; privatestringdate; privatestringprogrammer; }
//把屬性指派給類
[BugFixAttribute(121,"JesseLiberty","01/03/05")] [BugFixAttribute(107,"JesseLiberty","01/04/05", Comment="Fixedoffbyoneerrors")] publicclassMyMath {
publicdoubleDoFunc1(doubleparam1) { returnparam1+DoFunc2(param1); }
publicdoubleDoFunc2(doubleparam1) { returnparam1/3; }
}
publicclassTester { publicstaticvoidMain() { MyMathmm=newMyMath(); Console.WriteLine("CallingDoFunc(7).Result:{0}", mm.DoFunc1(7)); } }
輸出:
CallingDoFunc(7).Result:9.3333333333333339
象我們看到的那樣,屬性對輸出絕對沒有影響,創建屬性也不會影響代碼的性能。到目前為止,讀者也只是在聽我論述有關屬性的問題,使用ILDASM瀏覽元數據,就會發現屬性確實是存在的。
|