本文可以從技術文章下載出獲得,其中包含了一個使用定制屬性的Visual Studio項目示例文件。
屬性類是設計時可應用於類、properties和方法的特殊文類。屬性類提供描述元素某些方面屬性的方式或決定依附於該元素的其它類的行為,進而在運行時可以訪問和檢驗這些描述與行為。你可以將屬性類看作為類成員添加特殊修改器的一種方式。
例如,如果你曾經寫過Web服務,那肯定知道要使得方法在整個服務中是公開的,必須要使用WebMethod屬性。這是一個演示屬性應用的很好的例子,因為我們要用WebMethod屬性擴展編程模型。C#中沒有內建的方式來指定某個方法通過Web服務是可見的(因為內建有表明一個方法是私有的方式),因此需要添加WebMethod屬性來滿足這一需要。
設計自定義屬性
設計自定義屬性的過程十分簡單,在設計屬性前只需要考慮以下幾個方面:
◆使用屬性的目的是什麼?
屬性可以以很多方式使用。你需要定義屬性到底要完成什麼功能並確保這些特定功能沒有內建在.NET框架集中。使用.NET修改器要比使用屬性好,因為這將簡化同其它裝配件的集成過程。
◆屬性必須儲存什麼信息?
屬性是打算用來指示某個功能的簡單標志嗎?或者屬性是否要儲存信息?一個屬性可以擁有設計時賦予的一組信息,並在運行時查看這些信息。例如,看一下示例應用中的別名屬性。
◆屬性應該駐留在哪個裝配件中?
大多數情況下,可以將屬性包含在使用該屬性的裝配件中。不過也有這樣的例子,將屬性駐留在公共的、輕量級的、共享裝配件中會更好些。這種類型的配置允許客戶使用屬性時不必引用不需要的裝配件。
◆哪些裝配件將會識別屬性?
如果沒有模塊讀取屬性,那麼它將一文不值。你很可能將讀取屬性的類放在屬性駐留的同一個裝配件中。然而,正像前面提到的,也有這樣的例子,你想將讀取屬性的方法與屬性自身分別放在不同的裝配件中。
使用屬性
在我們深入了解如何設計自定義屬性之前,我們需要先看一下它們是如何使用的。例如,假定我們有一個稱為“Hide”的屬性它能夠有效地隱藏Properties,因此它們不會顯示在屏幕上。如果我們將這個屬性應用於“SSN”property,那麼代碼將會如列表A所示。
列表 A
[Hide ()]
public string SSN
{
get { return _ssn; }
set { _ssn = value; }
}
作為更復雜一點的例子,假設我們將有一個屬性稱為“Alias”。該屬性的任務是檢查一個property可能擁有別名。這將允許將一個property值映射給另一個property即使批roperty的名字不匹配。這個屬性接受一系列字符串值作為映射名。(列表B)
列表 B
[Alias ("FirstName", "First")]
public string FName
{
get { return _fName; }
set { _fName = value; }
}
在這個例子中,property“FName”被映射到“FirstName”和“First”,請查看示例應用以更詳細的了解這種應用。
創建屬性
創建屬性是一個簡單的過程。你可以定義繼承自System.Attribute類的一個包含你想要儲存的數據的類。列表C的前半部分顯示了如何創建一個名為“Alias”的屬性。
列表 C
Class Alias : System.Attribute
{
string[] _names;
public Alias(params string[] names)
{
this.Names = names;
}
public string[] Names
{
get { return _names; }
set { _names = value; }
}
}
正如你所看到的,這就是一個普通的類,唯一的例外就是繼承自System.Attribute類。我們不需要作任何特別的事情使它成為一個類。我們只是簡單的定義了一個需要使用的構造函數並創建了一個property和一個存儲數據的私有成員。
列表D是個更簡單的屬性——“Hide”屬性。這個屬性不需要構造函數(使用默認的構建函數),也不儲存數據。因為這個屬性只是一個簡單的標志類型的屬性。
列表 D
Class Hide : System.Attribute
{
//This is a simple attribute, that only requires
// the default constructor.
}
從代碼中讀取屬性
讀取屬性並檢查其中的數據比使用屬性或創建屬性顯著地更加復雜。讀取屬性要求開發人員要對如何使用一個對象的反射信息有個基本了解。如果你不熟悉反射機制,可以閱讀“應用反射”系列文章。
假設我們正在查看一個類,我們想知道該類的那個properties使用了Alias屬性以及都有哪些別名。列表E實現了這個功能。
列表 E
Private Dictionary<string, string> GetAliasListing(Type destinationType)
{
//Get all the properties that are in the
// destination type.
PropertyInfo[] destinationProperties = destinationType.GetProperties();
Dictionary<string, string> aliases = newDictionary<string, string>();
for each (PropertyInfo property in destinationProperties)
{
//Get the alias attributes.
object[] aliasAttributes =
property. GetCustomAttributes( typeof(Alias), true);
//Loop through the alias attributes and
// add them to the dictionary.
foreach (object attribute in aliasAttributes)
foreach (string name in ((Alias)attribute).Names)
aliases.Add(name, property.Name);
//We also need to add the property name
// as an alias.
aliases.Add(property.Name, property.Name);
}
return aliases;
}
這段代碼最重要的地方是對GetCustomAttributes的調用以及循環遍歷屬性提取別名的地方。
GetCustomAttributes方法可以在我們從對象類型中提取的PropertyInfo類中找到。在上面的應用中,我們將要查詢的屬性類型作為參數傳給GetCustomAttributes方法,同時還將“true”傳遞給該方法使得可以列出繼承的屬性。如何發現匹配的屬性,GetCustomAttributes方法將返回一個對象數組。還有另外一種超負荷方法可以列出property上的所有屬性,而不管屬性類型是什麼。
一旦有了屬性,我們需要檢查它們並從中提取需要的信息。這可以通過遍歷由GetCustomAttributes方法得到的對象數組並將每個對象映射成我們要查詢的屬性來完成。在完成映射後,我們就可以像訪問任意其它類的properties一樣來訪問屬性的properties。
正如我在前面所說,讀取屬性是最困難的部分。然而,一旦我們寫後讀取屬性的代碼後,將來回憶和實施起來就相當容易了。
應用示例
我強烈建議你下載本文包含的這個應用示例。這個應用示例在一個簡單的Windows應用中實現了下面的屬性,並演示了如何讀取和使用它們。
◆Alias——這同上面提到的Alias屬性一樣。當你需要將一種類型的對象翻譯為另一種類型時,需要使用該屬性。例如,如果你有一個Customer對象和一個Address對象,你可能需要將它們都翻譯為一個合並的包含人名和地址的Person對象,當一個不能使用直接映射時,可以使用該屬性。
◆DisplayName——示例代碼中包括檢查一個類實例並將它的property名稱與值輸出到屏幕上的代碼。這個屬性可用於覆蓋送到屏幕顯示的property名稱。例如,一個名為“FName”property可以使用DisplayName屬性,因此它顯示為“First Name”。
◆Examine——這個屬性使得示例應用中的PrintObject方法進入更深一層,並輸出使用了Examine屬性的property的值。例如,示例應用中的Customer對象將Examine屬性應用到Address property。這將指示PrintObject方法輸出address property中的所有信息。
◆Hide——這個屬性指示PrintObject()方法不要將當前property輸出到屏幕。該屬性用在Customer對象的SSN property上。
示例應用中包含了實現和讀取屬性每一步的注釋,仔細看一下,我敢保證你會發現一些在自己的應用中可以利用的功能。