見資源 給你的.NET代碼添加新的強大的功能和行為。 by Steven Yi 技術工具箱:C# .NET Fr" name="description" />

運用自定義的特性

發表于:2007-05-25來源:作者:點擊數: 標簽:代碼本文下載特性自定義
下載本文代碼 java script:openWindowRes('DotNetMagazine/2003_01/xml2html.asp?xmlfile=CustomAttribute/Resources.xmlxslfile=../../include/xsl/Resources.xsl')">見資源 給你的.NET代碼添加新的強大的功能和行為。 by Steven Yi 技術工具箱:C# .NET Fr
下載本文代碼
javascript:openWindowRes('DotNetMagazine/2003_01/xml2html.asp?xmlfile=CustomAttribute/Resources.xml&xslfile=../../include/xsl/Resources.xsl')">見資源
給你的.NET代碼添加新的強大的功能和行為。
by Steven Yi
技術工具箱:C#

.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類是可以串行的。注意類聲明前方括號中的特殊注釋。

using System;

[Serializable]
public class CatalogItem
{
   private String strName;
   private String strDesc;
   private int intItemID;
   private double dblPrice;
   public String Name
   {
      get { return strName; } 
      set { strName = value; }
   }
   public String Description
   {
      get { return strDesc; }
      set { strDesc = value; }
   }
   public int ItemID
   {
      get { return intItemID; }
      set { intItemID = value; }
   }
   public double Price
   {
      get { return dblPrice; } 
      set { dblPrice = value; }
   }
}


這個類有用于ItemID、Name、Description和Price的屬性。你也可以控制各個項目的呈現方式,用不同的排序標準(如名稱、價格等,以升序或降序)使目錄呈現不同的版本。CatalogItem列表的排序操作標準是由一個你創建的叫做CatalogSortAttribute的自定義的特性控制的,它描述了你想如何排列各個項目,排序規則(升序/降序)是怎樣的。


				圖1.
圖1. 擴展AbstractCatalog類
AbstractCatalog類型是基本的目錄類型,NameCatalog和PriceCatalog派生于它,它們代表了兩種目錄類型。每種目錄類型都包含一個CatalogItems數組,它派生于AbstractCatalog,你將根據你的目錄類型對這個數組進行排序(見下)。


圖 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特性可以限制我們只能在類和結構上運用這個自定義的特性。

using System;
[AttributeUsage(AttributeTargets.Class | 
   AttributeTargets.Struct)]
public class CatalogSortAttribute : System.Attribute
{
   private bool sortAscending;
   private string sortProperty;
   public CatalogSortAttribute()
   {
      sortProperty = "Name";
      sortAscending = true;
   }
   public CatalogSortAttribute(string sortproperty)
   {
      sortProperty = sortproperty;
      sortAscending = true;
   }
   public bool SortAscending
   {
      get { return sortAscending; }
      set { sortAscending = value; }
   }
   public string SortProperty
   {
      get { return sortProperty; }
      set { sortProperty = value; }
   }
}


你可以在構造器中為屬性定義缺省的值,然后你就可以很簡單地在你的目錄類中以多種方式運用這個特性了:
[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()方法提供了一些測試值。

using System;
public abstract class AbstractCatalog
{
   private CatalogItem[] catItems;
   public AbstractCatalog() { }
   public CatalogItem[] CatItems
   {
      get { return catItems; }
      set { catItems = value; }
   }
   public void SortItems()
   {
      DynamicSorter sorter = new 
         DynamicSorter();
      sorter.SortCatalog(this);
   } //end SortItems
   //Test method to create catalog items.
   public CatalogItem[] GetTestItems()
   {
      CatalogItem[] items = new 
         CatalogItem[10];
      //init items
      for (int i=0; i<10; i++)
      {
         items[i] = new CatalogItem();
      } //end for
      items[0].ItemID = 1;
      items[0].Name = "Leather Belt";
      items[0].Description = "Fine grain 
         brown leather";
      items[0].Price = 22.99;
      .
      .
      .
      return items;
   }
}


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特性來決定的。然后用一個快速排序算法來排列項目。

using System;
using System.Reflection;
using System.Collections;
public class DynamicSorter
{
   Comparer comparer;
   
   public DynamicSorter()
   {
      comparer = Comparer.Default;
   }
   public void SortCatalog(AbstractCatalog catalog)
   {
      Type type = catalog.GetType();
      CatalogSortAttribute[] sortAttrib = 
         (CatalogSortAttribute[]) 
   type.GetCustomAttributes(typeof(CatalogSortAttribute)
      , false);
      CatalogItem[] catItems = catalog.CatItems;
      this.QuickSort(catItems, 0, catItems.Length-1, 
         sortAttrib[0].SortProperty);
      if (!sortAttrib[0].SortAscending)
      {
         Array.Reverse(catItems);
      }
   }
   .
   . //The full code listing is available online.
   . 
}



				圖2.
圖2. 按名稱排序
現在我們來測試你的電子目錄程序。一旦調用SortItems()方法,NameCatalog的CatalogItems列表就打印出來了。測試類——TestSort.cs——創建了NameCatalog的一個實例并打印了排序目錄的結果(見列表3和圖2)。
C# :測試目錄結果

列表3. TestSort根據在NameCatalog的SortAttribute中定義的排序標準來輸出項目。

Public class TestSort
{
   public static void Main (String[] args)
   {
      NameCatalog cat = new NameCatalog()
      cat.CatItems = cat.GetTestItems();
      cat.SortItems();
      CatalogItem[] items = cat.CatItems;
      for (int i=0; i<items.Length; i++)
      {
         Console.WriteLine("ID: " + 
         items[i].ItemID + ", Name: " + 
         items[i].Name + ", Price: " + 
         items[i].Price);
      } //end for
      Console.ReadLine();
   }




圖 2. 按名稱排序

TestSort.cs的結果顯示了按名稱來排列目錄項目。注意NameCatalog.cs運用的[CatalogSortAttribute]注釋為排序設置了缺省的參數,說明排序是按照Name屬性、以升序進行的。


 

注意,你已經成功地按照名稱以升序排列了你的目錄項目。如果你進一步測試,修改TestSort類,用PriceCatalog進行排序,你會看到目錄項目會按照價格以降序進行排列了。運用你的自定義的特性——CatalogSortAttribute——你就可以用一行特性描述符來改變你的目錄類型的行為了。

自定義的特性可以給你的代碼添加新的強大的功能,盡管它們只不過是給你的代碼創建了額外的元數據描述符。我們面臨的問題是要識別在什么情況下一個自定義的特性可以使開發項目受益。一旦你找到運用特性的一個機會,通過將自定義的系統行為放在一個集中的位置,就可以使代碼開發過程變得更順利。當你需要添加特性描述符來影響對象的行為時,你的程序會變得更容易閱讀和維護。你不再需要每次為類似的情況重寫代碼了。


關于作者:
Steven Yi是位技術架構師,他在一家達拉斯的金融服務公司工作。他曾用J2EE和Microsoft解決方案為財富500的公司做過咨詢工作。作為一位MCSD、Java 2程序員Oracle 8i數據庫操作員,Steven在管理信息系統方面也有一定的造詣。他的聯系方式是syi@digitalinstance.com。

原文轉自:http://www.anti-gravitydesign.com

評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97