見資源 給你的.NET代碼添加新的強大的功能和行為。 by Steven Yi 技術工具箱:C# .NET Fr" name="description" />
下載本文代碼 javascript:openWindowRes('DotNetMagazine/2003_01/xml2html.asp?xmlfile=CustomAttribute/Resources.xml&xslfile=../../include/xsl/Resources.xsl')">見資源 |
.NET Framework的一個強大的功能之一就是它支持基于特性的編程,這就可以讓你在編譯時為你的代碼創建自定義的元數據。你可以用自定義的元數據給類提供額外的信息,從而進一步定義你的類型的行為。通過給運用特性(attribute)的類添加幾行代碼,你就可以很快地重用功能。我將講述如何通過將系統行為放在一個集中的位置,編寫自定義的特性來創建更容易閱讀的、可重用的、可擴展的代碼。通過學習這些特性是如何工作的,并且創建你自己的特性,你就可以更好地了解.NET Framework、.NET對元數據的運用以及C#語言的強大的特征。
.NET在實現特性時,它們是作為特殊的注釋出現在代碼中的,你可以將它們用于一個類型或成員字段。注釋是用方括號括起來的,描述了后面的代碼;.NET Framework或其它的程序可以在編譯時讀取它們,或者你可以用Reflection動態地讀取它們。特性可以描述一個特定的類型、一個類型的屬性(property)和方法、或者一個完整的集合。.NET已經實現了一些特性以支持其更強大的功能,如串行化和COM+ Enterprise Services。
運用.NET Reflection動態地讀取特性值可以讓你程序化地查看一個類型并讀取它的元數據。Reflection(位于System.Reflection名字空間中)在運行時查找類型,可以讓你得到一個特定類型的方法、字段、屬性、甚至是類型定義的特性。Reflection也可以讓你動態地讀取所定義的字段和屬性的值。
你可以根據特殊的需求創建自定義的特性,并在特性數據的基礎上執行自定義的操作。我將一步步地講述如何創建一個自定義的特性、動態地訪問它、并為一個虛構的電子目錄項目創建一個自定義的排序方法。這個例子用了一個基本的類,即CatalogItem。這個類很簡單,但是用.NET定義的[Serializable]特性修飾它后就可以讓它呈現一些強大的新的功能了,你單獨看代碼時,這些功能并不很明顯。通過添加這個特性,你就可以告訴.NET Framework你的CatalogItem類型可以被保存為一個二進制或基于字符的格式,如XML。
你將創建一個電子目錄,在這個電子目錄中,CatalogItem類代表每個項目(見下)。
clearcase/" target="_blank" >cccc99>C# :創建一個電子目錄
列表1. 在代碼中實現特性是個很簡單的過程。下面的代碼允許CatalogItem類是可以串行的。注意類聲明前方括號中的特殊注釋。
|
![]() |
|
圖 1. 擴展 AbstractCatalog類
這個電子目錄UML圖說明了NameCatalog和PriceCatalog是如何擴展AbstractCatalog抽象類的。DynamicSorter通過運用Reflection讀取項目值來排序目錄項目。
圖 1. 擴展 AbstractCatalog類
這個電子目錄UML圖說明了NameCatalog和PriceCatalog是如何擴展AbstractCatalog抽象類的。DynamicSorter通過運用Reflection讀取項目值來排序目錄項目。
CatalogSortAttribute類型是一個自定義的特性,通過將它插入到你的NameCatalog和PriceCatalog類型中,就可以控制每種目錄類型的排序結果了。
最后要說明的是,在你的程序中,DynamicSorter類型是很重要的。它用Reflection來讀取你自定義的CatalogSortAttribute屬性,從而對CatalogItems進行正確的排序。為了進一步說明Reflection的強大性,你可以用它來執行實際的排序。
創建自定義的特性
創建一個CatalogSortAttribute類型;根據C#命名慣例,你可以將它命名為[CatalogSort]或[CatalogSortAttribute](見工具條“創建特性的技巧”)。.NET定義了另外的特性來正確運用你自定義的特性。你可以將對特性的運用限制在類一級上、成員級上或通過運用System.AttributeUsageAttribute來將兩個級別結合起來。如果你沒有正確地運用特性,.NET編譯器就會拋出一個錯誤。創建你的CatalogSortAttribute,這樣它就只可以被用于類或結構(struct)。通過運用[AttributeUsage]特性,并從AttributeTargets枚舉傳入值來實現這點:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class CatalogSortAttribute : System.Attribute { . . . |
現在添加SortAscending和SortProperty屬性來提供關于按什么字段和順序進行排序的描述符(見下)。
C# :提供排序字段和方向
列表2. 屬性用sortProperty和sortAscending變量為CatalogSortAttribute.cs特性存儲參數選項。AttributeUsage特性可以限制我們只能在類和結構上運用這個自定義的特性。
|
[CatalogSort] //default constructor using default values [CatalogSort("Price")] //overloaded constructor, set SortProperty //to "Price" [CatalogSort("Price", SortAscending=false)] //overloaded constructor, set SortProperty //to "Price", and the SortAscending property to false. |
現在你需要創建不同的項目目錄,根據可用的CatalogSortAttribute選項對你的CatalogItems進行排序了。并不復雜的AbstractCatalog類型有一個屬性,可以用來得到(get)和設置(set) CatalogItem類型的一個數組,用SortItems()方法根據特性值來執行你的排序操作(見下)。
C#:創建一個AbstractCatalog類型
列表A. AbstractCatalog包含一個CatalogItems數組,并引用了DynamicSorter類,用Reflection來排序項目。GetTestItems()方法提供了一些測試值。
|
NameCatalog和PriceCatalog實現了自定義的特性信息:
using System; [CatalogSort] public class NameCatalog : AbstractCatalog { public NameCatalog() {} } [CatalogSort("Price", SortAscending=false)] public class PriceCatalog : AbstractCatalog { public PriceCatalog() {} } |
這兩個類都擴展了AbstractCatalog,并繼承了它的方法和屬性。CatalogSort特性為每個類定義了項目排列順序。用CatalogSortAttribute特性信息來修飾這些類,依靠DynamicSorter這個有用的類和.NET reflection來做更難的工作。NameCatalog根據Name屬性以升序排列項目,因為你只用了缺省的值。根據特性描述——[CatalogSortAttribute(“Price”,SortAscending=false)]——PriceCatalog根據價格以降序排列各個項目。
你的目錄類看上去很簡單,但是通過添加一個自定義的特性,你就可以只用一行代碼給它們提供一套新的行為了。
用Reflection訪問特性信息
現在讓我們來看看我們感興趣的部分——動態地讀取程序中你的自定義的特性。.NET Reflection可以讓你通過調用兩個方法來讀取特性信息。第一個方法是Type.GetCustomAttributes(System.Type,bool),它返回一個特定類型的自定義特性的一個對象數組。Boolean參數指定是否查詢類型的繼承鏈(inheritance chain)來找到特性,但在當前.NET版本中這個參數被忽略不計了。第二個方法是Type.GetCustomAttributes(bool),它返回自定義特性的一個對象數組,而不管類型是什么。
DynamicSorter用這個方法分別從你的目錄中讀取[CatalogSort]特性:
CatalogSortAttribute[] sortAttrib = (CatalogSortAttribute[]) type.GetCustomAttributes(typeof (CatalogSortAttribute), false); |
一旦讀取到特性,根據CatalogSortAttribute的Name和SortAscending屬性,你就可以知道按什么屬性和順序來排列你的目錄項目了。在這個例子中,我們用了一個快速排序算法,和一個以Type為基礎的Reflection來進行排序,它說明了.NET的其它的強大的Reflection功能,包括動態地返回一個類實例的屬性和字段數據。然后SortCatalog()方法可以重新排序你的CatalogItems數組,這個數組可以通過AbstractCatalog.CatItems屬性讀取到(見列表B)。
C# :讀取 CatalogSort Attribute
列表B. 排序參數的選擇是通過用Reflection讀取CatalogSort特性來決定的。然后用一個快速排序算法來排列項目。
|
![]() |
|
C# :測試目錄結果
列表3. TestSort根據在NameCatalog的SortAttribute中定義的排序標準來輸出項目。
|
圖 2. 按名稱排序
TestSort.cs的結果顯示了按名稱來排列目錄項目。注意NameCatalog.cs運用的[CatalogSortAttribute]注釋為排序設置了缺省的參數,說明排序是按照Name屬性、以升序進行的。
注意,你已經成功地按照名稱以升序排列了你的目錄項目。如果你進一步測試,修改TestSort類,用PriceCatalog進行排序,你會看到目錄項目會按照價格以降序進行排列了。運用你的自定義的特性——CatalogSortAttribute——你就可以用一行特性描述符來改變你的目錄類型的行為了。
自定義的特性可以給你的代碼添加新的強大的功能,盡管它們只不過是給你的代碼創建了額外的元數據描述符。我們面臨的問題是要識別在什么情況下一個自定義的特性可以使開發項目受益。一旦你找到運用特性的一個機會,通過將自定義的系統行為放在一個集中的位置,就可以使代碼開發過程變得更順利。當你需要添加特性描述符來影響對象的行為時,你的程序會變得更容易閱讀和維護。你不再需要每次為類似的情況重寫代碼了。
原文轉自:http://www.anti-gravitydesign.com