《隨筆七》——C#中的 “ 屬性 、 靜態屬性 ”
目錄
屬性
● 屬性是代表類的例項或類中的一個數據項的成員。屬性指的是一組兩個匹配的、稱為訪問器的方法:
public 返回型別 識別符號
{
set
{訪問器為屬性賦值
}
get
{訪問器為屬性獲取值
}
}
注意: public 這樣的修飾符是可選的,可以選擇私有的,這樣就不可以在類外直接訪問 set 和 get 訪問器了。但是間接 的。
注意: 訪問器不能被直接呼叫。
● set訪問器總是:
擁有一個單獨的、隱式的值參,名稱為value, 與屬性的返回型別相同。
set訪問器返回型別為void。
● get訪問器總是:
沒有引數。
擁有一個與屬性型別相同的返回型別。屬性型別返回的型別要跟你要返回的欄位的返回型別相同。
● 屬性跟欄位的相同點:
它是命名的例項成員
它有型別
它可以被賦值和讀取。
● 屬性跟欄位的不相同點:
屬性屬於一個成員函式
它不為資料儲存分配記憶體
它執行程式碼
訪問器的重點如下:
get 訪問器的所有執行路徑都必須包含一條return 語句,返回一個屬性型別的值。
訪問器 set 和 get 可以任何順序宣告, 並且只能有這兩個方法,不能有其他的。
要想不定義屬性的某個訪問器,可以忽略該訪問器的宣告。
兩個訪問器中至少有一個必須定義, 否則編譯器會產生錯誤資訊。
使用屬性
● 寫入和讀取屬性的訪問器時,會被隱式呼叫:
要寫入一個屬性,在賦值語句的左邊使用屬性的名稱。
要讀取一個屬性,把屬性的名稱用在表示式中。
注意: 不能顯式地呼叫訪問器,就跟呼叫函式那樣, 會出現編譯錯誤。
namespace Ch05Ex03
{
class D
{
private double name = 3.14; // 為欄位分配記憶體
public double MyValue // 屬性不分配記憶體
{
set
{
name = value; //返回型別為void
}
get
{
return name; // 返回型別是屬性的型別,屬性的型別也要跟欄位的資料型別一致
}
}
}
class Program
{
static void Main(string[] args)
{
D myD = new D();
myD.MyValue = 5.666; //給訪問器中的欄位賦值
WriteLine(myD.MyValue); //輸出賦值後的欄位的值
var tmp = myD.MyValue;
WriteLine(tmp); //輸出賦值後的欄位的值
var tt = myD.get(); // 錯誤, 不能這樣顯式呼叫
myD.set(5.3); // 錯誤, 不能這樣顯式呼叫
ReadKey();
}
}
}
● 我們經常將類中的欄位宣告private以封裝該欄位, 然後宣告一個public的屬性來控制從類的外部對該欄位的訪問。和屬性關聯的欄位稱為後備欄位或後備儲存。
● 屬性和後備欄位需要注意的問題有:
一種約定是兩個名稱使用相同的內容, 但欄位使用Camel 大小寫, 屬性使用 Pascal 大小寫。
另一種是欄位使用Camel 大小寫,並以下劃線開始,屬性使用 Pascal 大小寫。
private double nameField = 3.14; //第一種約定
public double NameFild
{
set
{
nameField = value;
}
get
{
return nameField;
}
}
private double _nameField = 3.14; // 第二種約定
public double NameFild
{
set
{
_nameField = value;
}
get
{
return _nameField;
}
}
利用 set 和 get 訪問器 執行其它運算
● 屬性訪問器不僅僅只可以對關聯的欄位傳入傳出資料, 還可以執行任何計算,或者不執行任何計算。但是get訪問器必須返回一個屬性型別的值。
下面看一個更有用的示例:
namespace Ch05Ex03
{
class D
{
private double _nameField = 3.14;
public double NameFild
{
set
{
_nameField = value > 100 ? 100 : value; //執行計算
}
get
{
return _nameField;
}
}
}
class Program
{
static void Main(string[] args)
{
D myD = new D();
myD.NameFild = 200; //給訪問器中的欄位賦值
WriteLine(myD.NameFild); //輸出賦值後的欄位的值
ReadKey();
}
}
}
只讀和只寫屬性
● 要想不定義屬性的某個訪問器, 可以忽略該訪問器的宣告。
● 只有get訪問器的屬性是隻讀屬性。它是安全的, 只傳出資料。
● 只有set訪問器屬性是隻寫屬性, 它是安全的, 把一項資料從類的外部傳入類, 而不允許太多訪問方法。
屬性和公共欄位
● 屬性比公共欄位更改,理由如下:
屬性屬於成員函式而不是資料成員, 允許你處理輸入和輸出,而公共欄位不行。
屬性可以只讀或只寫,而欄位不行。
編譯後的變數和屬性語義不同。
自動實現屬性
● 自動實現屬性: 允許只宣告屬性而不聲明後備欄位。 編譯器會為你建立隱藏的後備欄位, 並且自動掛接到get 和 set 訪問器上。
自動實現屬性的要點如下:
不聲明後備欄位—— 編譯器根據屬性的型別分配儲存。
不能提供訪問器的方法體—— 它們必須被簡單地宣告為分號。 get 相當於簡單的記憶體讀, set 相當於簡單的記憶體寫。
除非通過訪問器,否則不能訪問後備欄位。 因為不能用其他的方法訪問它, 所以實現只讀和只寫屬性沒有意義, 因此必須同時提供讀寫訪問器。
namespace Ch05Ex03
{
class D
{
public double NameFild //分配記憶體
{
set;get;
}
}
class Program
{
static void Main(string[] args)
{
D myD = new D();
myD.NameFild = 200; //給訪問器中的欄位賦值
WriteLine(myD.NameFild); //輸出賦值後的欄位的值
ReadKey();
}
}
}
● 除了方便之外, 自動實現屬性使你在傾向於宣告一個公有欄位的地方很容易插入一個屬性。
靜態屬性
● 屬性也可以宣告為 static, 靜態屬性的訪問器和所有靜態成員一樣,具有以下特點:
不能訪問類的例項成員——可以訪問類的靜態例項成員,靜態例項成員可以在非靜態方法中被訪問。
不管類是否有例項,它們都是存在。
當從類的外部訪問時,必需使用類名引用,而不是例項名。
namespace Ch05Ex03
{
class D
{
public static int MyValue
{
get; set;
}
public void PrintValue()
{
WriteLine($"輸出值為:{MyValue}");
}
}
class Program
{
static void Main(string[] args)
{
WriteLine($"先輸出靜態屬性的值:{D.MyValue}");
D.MyValue = 10; // 在類的外部設定靜態屬性的值
WriteLine($"再輸出靜態屬性的值:{D.MyValue}");
ReadKey();
}
}
}
再看一個示例程式:
namespace Ch05Ex03
{
class D
{
int aa = 12;
static int bb;
public static void Show()
{
WriteLine($"輸出bb的值:{bb}");
//WriteLine($"輸出bb的值:{aa}"); 錯誤,靜態成員函式不可以輸出非靜態成員資料
}
public void PrintValue()
{
WriteLine($"輸出bb的值:{bb}"); // 非靜態的成員函式可以輸出靜態欄位的值
WriteLine($"輸出aa的值:{aa}");
}
public static int MyValue
{
set
{
//aa = value; //錯誤,靜態屬性不可以訪問非靜態欄位
bb = value; //正確,靜態屬性可以訪問靜態欄位
}
get
{
return bb;
}
}
}
class Program
{
static void Main(string[] args)
{
WriteLine($"先輸出靜態屬性的值:{D.MyValue}");
D.MyValue = 100; // 在類的外部設定靜態屬性的值
WriteLine($"再輸出靜態屬性的值:{D.MyValue}");
D myD = new D();
myD.PrintValue();
ReadKey();
}
}
}
輸出結果為:
先輸出靜態屬性的值:0
再輸出靜態屬性的值:100
輸出bb的值:100
輸出aa的值:12