C Sharp/屬性
屬性(property)是一種用於訪問對象或類的內容的機制。可以像使用公共數據成員一樣使用屬性,但實際上屬性是稱為「訪問器」的一種特殊方法。
屬性(property)是一種"高級欄位」,它可能帶有一個 getter 和一個 setter,它們保護屬性的值,使之不會被外部胡亂篡改。
和欄位相比,屬性實現了對成員的封裝。
無參屬性
[編輯]class A
{
private int c;
public int getC()
{
return c;
}
public void setC(int value)
{
c = value;
}
}
在這裡的私有欄位稱為支持欄位(Backing Field)。
不過,這樣做有兩個明顯缺點,一是必須手打這些代碼;二是在訪問屬性時,必須調用方法,而不能直接使用點號加屬性名。
CLR 提供了稱為屬性的機制,解決了這兩個缺點。下面的寫法是經過簡化了的寫法:
private int c { get; set; }
這樣創建的屬性叫做自動實現的屬性。可以直接通過 A.c 訪問屬性,而非使用 A.getC 和 A.setC 方法了。
實際上,無參屬性僅僅是語法糖。通過編譯之後使用 iladsm 查看,我們可以發現,編譯器自動為我們生成了 get_c 和 sct_c 方法,以及一個支持欄位k_BackingField。
只讀和只寫屬性
[編輯]可以通過將 get 或 set 設置為 private 獲得只讀和只寫屬性。
例如,如果 set 是私有的,則屬性就是只讀的。不過,屬性的值仍然可以被類型內部的成員修改:
public int c { get; private set; }
從 C# 6 開始,允許省略 set 獲得真正具有不變性的屬性:
public int c { get; }
此時這個欄位就真正地具有了不變性,當你初始化了這個欄位的值之後,就再也無法更改它的值。
帶有邏輯的屬性
[編輯]通過為屬性的 get 和 set 中加入代碼,可以控制屬性的取值範圍。例如,要實現一個永遠為非負整數的屬性:
private int age;
public int Age
{
get
{
return age;
}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("Age", value, "Age必须大于等于0");
}
age = value;
}
}
在這段代碼中,關鍵字 value 代表賦值時傳入的新值。
有參屬性
[編輯]有參屬性的 get 方法支持傳入一個或更多參數,set 方法支持傳入兩個或更多參數。
C# 中的有參屬性又叫索引器,顧名思義,它是重載[]操作符的一種方式。
雖然有參屬性(索引器)很少被使用,不過,它有一個常用場景是這樣的:一個類的成員包括一個集合。比如,記錄每天 24 小時溫度的 DayTemperature 類,具有一個長度為 24 的 double 集合成員 Temperatures。當拿到這個類的一個實例 d 時,訪問它的成員需要 d.Temperatures[6](代表早上 6 點的溫度)。如果對這個類實現有參屬性,可以直接使用 d[6] 獲得相同的值,省去了集合成員名稱 Temperatures。這種表示法不僅簡化了客戶端應用程式的語法,還使其他開發人員能夠更加直觀地理解類及其用途。
索引器至少要定義一個訪問方法(即 get 或 set)。
class Program
{
static void Main(string[] args)
{
var d = new DayTemperature();
d.temperature[0] = 20.5;
d.temperature[1] = 22;
//使用类索引器访问
Console.WriteLine(d[1]); //22
Console.ReadKey();
}
}
class DayTemperature
{
public double[] temperature = new double[24];
//类的索引器
public double this[int index]
{
get
{
//检查索引范围
if (index < 0 || index >= temperature.Length)
{
return -1;
}
else
{
return temperature[index];
}
}
set
{
if (!(index < 0 || index >= temperature.Length))
{
temperature[index] = value;
}
}
}
}
屬性的意義
[編輯]通過屬性的封裝,保留了它與外部交互的能力,又實現了一種可靠的讀寫機制。私有的欄位、屬性和方法保護了這些成員,使它們不會被外界調用,因為這是外界無需知道的信息。
通過封裝,類型只需要向外部提供它應該知道的信息,否則就會出現「你知道的太多了」這種情形。