我們的目標:儘可能編寫出運行效率更高,更健壯,更容易維護的C#代碼。
原則一:儘可能的使用屬性(property),而不是資料成員(field)。
Always use properties instead of accessible data members.
出於以下幾點原因,請在設計類時,儘可能的使用屬性,而不是成員。
1、.Net對屬性的支援遠遠大於對成員的支援,你可以對屬性進行資料繫結,設計時說明等很多資料成員不被支援的內容。看看.net裡的屬性面板,你會明白的。
2、資料安全性檢測;
屬性本質上是兩個函數,只是因為C#的特殊文法,但我們可以像訪問成員一樣的訪問它。因此我們可以在屬性設計上添加更多更靈活的內容,對屬性進行管理。其中對屬性資料的檢測是其中之一。
在對資料檢測時,如果探索資料不滿足條件,最好以拋出異常的形式來解決,千萬不要設定預設值,這是很危險的事情。例如:
public string Name{
get{
if(this._Name==null){
return “NoName”;
}else{
return this._Name;
}
}
set{
if(value==null){
this._Name = “NoName”;
}else if(value.Length<=10){
this._Name = value;
}else{
this._Name = value.SubString(0,10);
}
}
}
看上去很不錯不是嗎?請馬上動手修改你的代碼吧,上面的代碼是很危險的!或者你還不明白,明明對資料進行了安全檢測,為什麼還是危險的呢?試想這樣的情況,有兩個執行個體o1與o2, o1的Name為null,我們做了這樣的事:o2.Name = o1.Name;
結果是什嗎?o2.Name為”NoName”,而在本質上,o1與o2的Name是根本不相等的。這會對後面的程式運行帶來很大的麻煩。請以拋出異常的形式來解決資料不滿足條件時的問題。
3、線程同步
對執行個體的屬性可以進行線程同步,而與訪問者無關。例如:
public string Name{
set{
lock(this){
//…
}
}
}
當然,你完全可以在類的外面進行線程同步,但那樣的工作量遠比上面的方法大得多。推薦你使用上面的方法進行線程同步,如果要對靜態成員同步,請用lock(typeof(MyClass))方法。
4、屬性可以是抽象的,而資料成員不能是抽象的,這為我們設計出相容性更強,擴充性更強的類提供了好的解決方案。
5、屬性可以以介面的形式表現。介面裡不能定義資料成員,這在一定程度上限制我們的設計。請用屬性來解決這個問題吧。
6、基於屬性的索引。索引器是C#文法的特殊內容,而索引器正是通過屬性來完成的,如果你想為你的類添加一個索引器,除了用屬性你還能用什麼呢?
7、最後一點,也是比較重要的。不要直接把公用成員轉化成屬性。
看了上面的說明,是不是覺得應該馬上把所有的資料成員都修改成屬性了呢?在你修改前一定要閱讀下面的內容。
不要直接把公用成員轉化成屬性。它們在C#的原始碼級上的文法雖然是相同的,但在IL上是不同的。即:o1.Name(屬性)和o1.Name(成員),雖然C#代碼一樣,但IL的代碼是不一樣的。所以,當你準備修改所有的資料成員時,必須重新編譯類的代碼,同時也要重新編譯訪問該類執行個體的所有相關代碼。所以,在你設計時不要為了方便,想先用資料成員使用,然後在後期再修改成屬性,這不是一個好主意。
最後,不要為因為使用屬性而使IL代碼多了幾行而擔心。雖然資料成員在訪問上比屬性要快一點,但在實際運行時,不僅對人覺得是一樣的,對機器而言感覺也像是一樣的快,因為我的CPU越來越快了,完成一個函數調用太快了,跟完成一個資料訪問一樣的快(當然,這個函數得足夠小,就跟我們的屬性一樣),呵呵。
OK,關於使用屬性的問題就先說這些。