程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Visual C#中定義和使用自己的特性(2)

Visual C#中定義和使用自己的特性(2)

編輯:關於C語言
定義特性vs.類的屬性

在特性和類的屬性之間存在著明顯相似的地方。這給我們何時,何處應該使用自定義特性帶來了困惑。開發者們通常引用一個類的屬性,並把屬性的值作為自己“特性”,那麼屬性和特性之間真正的區別在哪裡呢?

當你定義特性的時候,它和屬性沒有根本的區別,使用時,可以以相同的方式把它附加到程序集不同的類型上,而不僅僅在類上使用。Table2列舉了可以應用特性的所有程序集類型。

Table 2:可以應用特性的所有程序集類型。

Type

Assembly

Class

Delegate

Enum

Event

Interface

Method

Module

Parameter

Constructor

FIEld

Property

ReturnValue

Structure

讓我們從清單中挑選一個作為例子。你可以在參數上應用特性,這看起來很微小,好像是在給參數添加屬性?其實,這是一個新穎的,非常不錯的主意,因為你不會用類的屬性做這件事。這裡也突出了特性和屬性之間很大的不同之處,因為屬性僅僅是類的一個成員而已。它們不能與一個參數,或者清單中列舉的其他類型關聯起來,當然,這要把類排除在外。

在另外的方面,類的屬性被限制在運行的環境下,而特性卻沒有被限制。通過定義,一個屬性就依賴於特定的類,這個屬性僅僅可以通過類的實例訪問,或者通過該類派生類的實例訪問。另一方面,特性卻可以應用到任何地方。在assembly類型上應用特性,以檢驗是否與自定義特性中的相匹配,這對於assembly類型來說,是最適合的了。在下一部分,我將更多的討論自定義特性類中的ValidOn屬性。在面向組件的開發中,這是非常有用的,因為特性的這個特征將更加促進松耦合。

特性和屬性之間另外的一個不同的地方將涉及到它們各自存儲的值。屬性成員的值是一個實例化的值,在運行時,是可以被改變的。而特性的值,是在設計時(在源代碼裡)設定,然後直接把這些特性的值編譯成元數據保存到程序集裡。之後,你將不能改變這些特性的值。實際上,你已經把這些特性的值,變成硬編碼的、只讀的數據。

考慮一下,你應用特性的時候。舉個例子,在一個類定義的時候,給其附加了一個特性,那麼該類的每一個實例都會擁有相同的分配給此特性值,而不論你實例化該類的多少個實例。你不能把特性附加到一個類的實例上,你只可以在類型/類的定義上應用特性。

創建一個自定義特性類

現在,綜合以上的描述,我們將演示一個更實際的實現過程。讓我們創建一個自定義特性類。該特性會保存一些關於代碼修改的跟蹤信息,在源代碼裡,這些都將作為注釋。在這個例子裡,我們將僅僅記錄一些條目:缺陷id,開發者id,修改的日期,導致缺陷的原因,以及有關修正的注釋。為了保持例子足夠的簡單,我們將關注於如何創建一個自定義特性類(DefectTrackAttribute),而該特性類僅被用於類和方法上。

DefectTrackAttribute定義的代碼如下:

using System;
namespace MyAttributeClasses
{
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple = true)]
public class DefectTrackAttribute :Attribute
{
private string cDefectID ;
private DateTime dModificationDate ;
private string cDeveloperID ;
private string cDefectOrigin ;
private string cFixComment ;
public DefectTrackAttribute ()
{
}
public DefectTrackAttribute(
string lcDefectID,
string lcModificationDate,
string lcDeveloperID )
{
this.cDefectID = lcDefectID ;
this.dModificationDate =
System.DateTime.Parse( lcModificationDate ) ;
this.cDeveloperID = lcDeveloperID ;
}
public string DefectID
{
get { return cDefectID ; }
}
public string ModificationDate
{
get
{
return dModificationDate.ToShortDateString() ;
}
}
public string DeveloperID
{
get { return cDeveloperID ; }
}
public string Origin
{
get { return cDefectOrigin ; }
set { cDefectOrigin = value ; }
}
public string FixComment
{
get { return cFixComment ; }
set { cFixComment = value ; }
}
}
}

如果你之前沒有接觸過特性,那麼你將對下面的代碼有點陌生。

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple = true)]

這一行代碼,把特性[AttributeUsage]附加到特性類的定義上。方括號的語法表明一個特性的構造器被調用。所以,特性類也可以擁有它們自己的特性,這看起來可能有點混淆,但是隨著我給你展示可以用特性類來做些什麼,你對它的認識,將會越來越清晰。

[AttributeUsage]特性具有一個定位參數和兩個命名參數。定位參數指定了特性類將被用於何種類型,定位參數的值是枚舉AttributeTargets的組合。在我的例子裡,我僅僅把特性類應用在類和方法上,所以通過組合兩個AttributeTargets的值的滿足了我的要求。

[AttributeUsage]特性的第一個命名參數是AllowMultiple,該參數指定了是否可以在同一個類型上應用多次(你所定義的)特性類。默認值是false,即不允許應用多次。但是,根據這個例子的實際情況,你將會在某一類型上不止一次的應用特性(DefectTrackAttribute),所以應該使用[AttributeUsage]的命名參數AllowMultiple,並將其設置為true。這是因為,一個特定的類和方法在其生命周期裡會經歷多次修訂,所以你需要使用[DefectTrackAttribute]特性記錄每一次變化。

[AttributeUsage]特性的第二個命名參數是Inherited,它指定了派生類(使用此特性類的子類)是否繼承此特性。我使用了此參數的默認的值false。因為我使用的是默認值,所以也就不需要指定該命名參數。為什麼不需要繼承呢?我想獲取源代碼的修改信息是跟每一個具體的類和方法有關的。如果把Inherited設為true,那麼開發者將會混淆一個類的[DefectTrackAttribute]特性,無法辨別[DefectTrackAttribute]特性是它自己的還是從父類繼承的。

上面的代碼展示了特性類(DefectTrackAttribute)的定義。它繼承於System.Attribute,事實上,所有的特性均直接或間接的繼承於System.Attribute。

上面的代碼裡,還定義了特性的5個私有的字段,這些字段均用於保存與特性相關的值。

在我們特性類中第一個方法是構造器,它是帶有3個參數的簽名。構造器的參數對於特性類而言,就是這個特性的定位參數,這些參數是強制性的。如果你願意,你可以重載構造器,使其可以擁有更多的有關定位參數配置的選擇。

我們的特性類中剩下的部分就是一些公有屬性的聲明,這些屬性與類中的私有字段相對應。當你查閱元數據的時候,你可以使用這些屬性訪問該特性的值。需要說明的是,對應定位參數的屬性沒有set語句,只有get語句。這就導致了這些屬性是只讀的,這也與它們是定位參數而不是命名參數的含義相一致。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved