1.什麼是屬性
定義:是字段和方法的交集—— 看起來像字段,用起來像方法。
訪問屬性所用的語法和訪問字段一樣。然而,編譯器會將這種字段風格的語法自動轉換成對特定訪問器的調用。
訪問器:取值和獻值方法統稱爲訪問器方法。兩個方法有時也稱爲get訪問器和set訪問器,或者getter 和setter。
屬性的聲明如下所示:
AccessModifier Type ProperName
{
get
{
//取值代碼
}
set
{
//賦值代碼
}
}
屬性可以包含兩個代碼塊,分別以get和set關鍵字開頭。其中,get 塊包含讀取屬性時執行的語句,set塊包含在向屬性寫入時執行的語句。屬性的類型指定了由get和set訪問器讀取和寫入的數據的類型。
屬性和字段名稱的注意事項:
例如一下代碼,它實現了名爲Employee的類。EmployeeID 屬性提供對私有字段employeeID字段的公共訪問,
class Employee
{
private int employeeID;
public int EmployeeID
{
get { return this. EmployeeID; }
set { this. EmployeeID = value; }
}
}
代碼編譯沒有問題,但每次訪問EmployeeID屬性都會拋出StackoverflowException異常,這是由於get和set訪問器不小心引用屬性(以大寫字母E開頭)而不是私有字段(小寫e),這造成了無限遞歸,最終造成可用內存被耗盡。這種bug是很難發現的!有鑑於此,我們以下劃線開頭命名爲屬性提供數據的私有字段。這樣可以更加明顯地和屬性進行區分。除此之外的其他所有私有字段還是使用不以下劃線開頭的camelCase 標識符。
1.1使用屬性
在表達式中使用屬性時,要麼從中取值,要麼向其賦值。下例從ScreenPosition結構的X和Y屬性中取值:
ScreenPosition origin=new ScreenPosition(e=0, 0);
int xpos =origin.x; // 實際調用origin.X. get
int ypos=origin.Y; // 實際調用origin.Y.get
注意,現在屬性和字段是用相同的語法來訪問。從屬性取值時,編譯器自動將字段風格的代碼轉換成對屬性的get訪問器的調用。類似地,向屬性賦值時,編譯器自動將字段風格的代碼轉換成對該屬性的set訪問器的調用:
origin.X=40;//實際調用origin.X.set. value 設爲40
origin.Y=100; //實際調用origin.Y.set, value 設爲100
如前所述,要賦的新值通過value變量傳給set訪問器。“ 運行時”自動完成傳值。
還可同時對屬性進行取值和賦值。在這種情況下,get和set訪問器都會被用到。例如,編譯器自動將以下語句轉換成對get和set訪問器的調用:
origin.X +=10;
提示:可採取和聲明靜態字段及方法一樣的方式聲 明靜態屬性。訪問靜態屬性時,要附加類或結構名稱作爲前綴,而不是附加類或結構的實例名稱作爲前綴。
1.2只讀屬性
可以聲明只包含get 訪問器的屬性,這稱爲只讀屬性。例如,以下代碼將ScreenPosition結構的X屬性聲明爲只讀屬性:
struct ScreenPosition
{
private int. x;
public int X
{
get { return this. X; }
}
}
X屬性不含set訪問器,向X寫入會報告編譯時錯誤,例如:
origin.x =140; //編譯時錯誤
1.3只寫屬性
類似地,可聲明只包含set 訪問器的屬性,這稱爲只寫屬性。例如,以下代碼將ScreenPosition結構的x屬性聲明爲只寫屬性:
struct ScreenPosition
{
private int. x;
...
public int X
{
set { this. x = rangeCheckedX(value); }
}
}
X屬性不包含get訪問器。所以,讀取X會報告編譯時錯誤,例如:
Console.WriteLine(origin.X);//編譯時錯誤
origin.X = 200; //編譯通過
origin.X += 18;//編譯時錯誤
注意:只寫屬性適合對密碼這樣的數據進行保護。理想情況下,實現了安全性的應用程
序允許設置密碼,但不允許讀取密碼。登錄時用戶要提供密碼。登錄方法將用戶提供的密碼與存儲的密碼比較,只返回兩者是否匹配的消息。
1.4屬性的可訪問性
聲明屬性時要指定可訪問性(public, private 或protected)。但在屬性聲明中,可爲get和set訪問器單獨指定可訪問性,從而覆蓋屬性的可訪問性。例如,下面這個版本的ScreenPosition結構將x和Y屬性的set訪問器定義成私有,而get訪問器仍爲公共(因爲屬性是公共的):
struct ScreenPosition
{
private int _x, _y;
…
public int X
{
get { return this._x; }
private set { this._x=rangeCheckedX(value); }
}
public int Y
{
get { return this._y; }
private set { this._y=rangeCheckedY(value); }
}
…
}
爲兩個訪問器定義不同的可訪問性時,必須遵守以下規則。
(1)只能改變 一個訪問器的可訪問性。例如,將屬性聲明爲公共,但將它的兩個訪問器都聲明成私有是沒有意義的。
(2)訪問器 的訪問修飾符(也就是public, private或者protected)所指定的可訪問性在限制程度上必須大於屬性的可訪問性。例如,將屬性聲明爲私有,就不能將get訪問器聲明爲公共(相反,應該屬性公共,set訪問器私有)。
2.理解屬性的侷限性
屬性在外觀、 行爲和感覺上都像字段。但屬性本質上是方法而不是字段。另外,屬性存在以下限制。
(1)只有在結構或類初始化好之後,才能通過該結構或類的屬性來賦值。下例的代碼非法,因爲結構變量location尚未使用new來初始化:
ScreenPosition location;
location.X = 40; //編譯時錯誤,location 尚未賦值
(2)不能將屬性作爲ref或out參數值傳給方法:但可寫的字段能作爲ref或out參數值傳遞。這是由於屬性並不真正指向一個內存位置:相反,它指向的是一個訪問器方法,例如:
MyMethod(ref location.X); // 編譯時錯誤
(3)屬性最多隻能包含一個get和一個set訪問器。不能包含其他方法、字段或屬性。
(4)get和set訪問器不能獲取任何參數。要賦的值會通過內建的、隱藏的value變量自動傳給set訪問器。
(5)不能聲明const 屬性,例如:
const int X{ get { ... }set { ...} } //編譯時錯誤
3.在接口中聲明屬性
接口除了能定義方法,還能定義屬性。
例如:
interface IScreenPosition
{
int X{get;set;}
int Y{get;set;};
}
實現該接口的任何類或結構都必須實現X和Y屬性,並在屬性中定義get和set訪問器,例如:
class ScreenPositlon : IScreenPosition
{
public int X
{
get{...}
set{...}
}
public int Y
{
get{...}
set{…}
}
…
}
用途:例如可以用屬性替代方法
4.生成自動屬性
C#語言的設計者知道程序員都是“大忙人”,不該花時間寫多餘的代碼。所以,C#編譯器現在能自動爲屬性生成代碼,如下所示:
class Circle
{
public int Radius{ get; set; }
…
}
在這個例子中,Circle 類包含名爲Radius的屬性。除了屬性的類型,不必指定這個屬性是如何工作的一get 和set訪問器都是空白的。C#編譯器自動將這個定義轉換成私有字段以及一個默認的實現,如下所示:
class Circle
{
private int _radius;
public int Radius{
get
{
return this._ radius;
}
set
{
this._radius = value;
}
}
…
}
所以,只需寫很少的代碼就能實現簡單屬性。以後如果添加了額外的邏輯,也不會干擾現有的任何應用程序。
參考書籍:《Visual C#從入門到精通》