通路器可以控制對這個字段的通路,例如需要滿足一些條件才能get到值
屬性擁有兩個類似于函數的塊,一個塊用于擷取屬性的值,另一個塊用于設定屬性的值。這兩個塊也稱為通路器,分别用get和set關鍵字來定義,可以用于控制對屬性的通路級别。可以忽略其中的一個塊來建立隻讀或隻寫屬性(忽略get塊建立隻寫屬性,忽略set塊建立隻讀屬性)。當然,這僅适用于外部代碼,因為類中的其他代碼可以通路的資料。還可以在通路器上包含可通路修飾符,例如使get塊變成公共的,把set塊變成保護的。隻有包含其中一個一個塊,才能獲得有效屬性(既不能讀取也不能修改的屬性沒有任何用途)。
屬性的基本結構包括标準的可通路修飾符(public、private等),後跟類名、屬性和get塊(或set塊,或者get塊和set塊,其中包含屬性處理代碼)。
public intmyIntProp
{
get
{
//Property getcode.
}
set
{
//Property setcode.
}
}
1 get關鍵字
get塊必須有一個屬性的傳回值,簡單的屬性一般與私有字段相關聯,以控制對這個字段的通路,此時get塊可以直接傳回該字段的值,例如:
private int myInt;
public intmyIntProp
{
get
{
return myInt;
}
set
{
//Property setcode.
}
}
類外部的代碼不能直接通路這個myInt字段,私有的,必須使用屬性來通路該字段。
2 set關鍵字
set函數以類似的方法把一個值賦給字段。這裡使用value表示使用者提供的屬性值:
private int myInt;
public intmyIntProp
{
get
{
return myInt;
}
set
{
myInt = value;
}
}
value等于類似與屬性相同的值,是以如果屬性和字段使用相同的類型,就不必擔心資料類型轉換了。
這個簡單的屬性隻能直接通路myInt字段。在對操作進行更多的控制的時候,屬性的真正作用才能發揮出來,例如,使用下面的代碼實作set塊:
set
{
if(value >= 0&& value <= 10)
myInt = value;
}
隻用賦給屬性的值在1~10之間,才會改myInt。此時,要做一個重要的設計選擇:如果使用了無效值,該怎麼辦:
· 什麼也不做
· 給字段賦預設值
· 繼續執行,就好像沒有發生錯誤一樣,但記錄下來該事件,以備将來分析
· 抛出異常
一般情況下,後面兩個選擇效果較好,選擇哪個選項取決于如何使用類,以及給使用者授予多少控制權。抛出異常給使用者提供的控制權相當的大,例如:
set
{
if(value >= 0&& value <= 10)
myInt = value;
else
throw (newArgumentOutOfRangeException("myIntProp",value,"myIntProp must beassigned a value between 0 and 10."))
}
這可以在使用屬性的代碼中通過try...catch...finaly邏輯來處理。
注:屬性可以使用virtual、override和abstract關鍵字,就像方法一樣,但這幾個關鍵字不能用于字段。最後,如上述,通路器可以有自己的通路性。
執行個體:
public classMyClass
{
public readonly string Name;
private int intVal;
public int Val
{
get
{
return intVal;
}
set
{
if (value >= 0 && value <= 10 )
intVal = value;
else
throw (new ArgumentOutOfRangeException("Val",value,"Valmust be assigned a value between 0 ang 10."));
}
}
public override string ToString()
{
return "Name:"+Name+"\nVal:"+Val;
}
private MyClass(): this("Default Name")
{
}
public MyClass(string newName)
{
Name = newName;
intVal = 0;
}
}
static voidMain(string[] args)
{
Console.WriteLine("Creating object myobj...");
MyClass myObj = new MyClass("My Object");
Console.WriteLine("myObj created.");
for (int i = -1; i <= 0; i++ )
{
try
{
Console.WriteLine("\nAttempting to assign {0} tomyObj.val...",i);
myObj.Val = i;
Console.WriteLine("Value {0} assigned to myObj.val.",myObj.Val);
}
catch(Exception e)
{
Console.WriteLine("Exception {0} throw.",e.GetType().FullName);
Console.WriteLine("Message:\n\"{0}\"",e.Message);
}
}
Console.WriteLine("\nOutputting myObj.ToString()...");
Console.WriteLine(myObj.ToString());
Console.WriteLine("myObj.ToString() Output.");
Console.ReadKey();
}
Main()中的的代碼建立并使用在MyClass.cs中定義的MyClass類的執行個體。執行個體化這個類必須使用非預設的構造函數來進行,因為MyClass類的預設構造函數是私有的。
Main()試着給myObj(MyClass的執行個體)的Val屬性指派。for循環在兩次中指派-1和0,try..catch...結構用于檢測抛出的異常。把-1賦給屬性時,會抛出System.ArgumentOutOfException類型的異常,catch塊中的代碼會把改異常的資訊輸出到控制台視窗中。在下一個循環中,值0成功的賦給了Val屬性,通過這個屬性再把值賦給私有字段intVal。
3 自動屬性
屬性是通路對象狀态的首選方式,因為他們禁止外部代碼實作對象内部的資料存儲機制。屬性還對内部資料的通路事施加了更多的控制,但是,一般以非常标準的方式屬性,即通過一個公共屬性來通路一個私有成員。其代碼非常類似于前面的代碼,這是VS重構工具自動生成的。
重構功能肯定加快了鍵入的速度,C#還為此提供了另一種方式:自動屬性。利用自動屬性,可以用簡化的文法聲明屬性,C#編譯器會自動添加未鍵入的内容,具體而言,編譯器會聲明一個用于存儲屬性的私有字段,并在屬性的get和set塊中使用該字段,我們無需考慮細節。
public intMyIntProp
{
get;
set;
}
我們按照通常的方式定義屬性的可通路性、類型和名稱。但是沒有給get和set塊提供實作的代碼。這些塊的實作代碼(和底層的字段)由編譯器提供。
使用自動屬性時,隻能通過屬性通路資料,不能通過底層的私有字段來通路,我們不知道底層私有字段的名稱(該名稱是編譯期間定義的)。但這并不是一個真正意義上的限制,因為可以直接使用屬性名。自動屬性的唯一限制是他們必須包含get和set存儲器,無法使用這種方法定義隻讀和隻寫屬性。