1. 程式人生 > >讀書筆記之《編寫高質量程式碼:改善C#程式的157個建議》

讀書筆記之《編寫高質量程式碼:改善C#程式的157個建議》

最近,在閱讀書籍《編寫高質量程式碼:改善C#程式的157個建議》,感覺寫得很不錯,特將其中的建議整理了一下,待以後隨時檢視。

現只羅列了其中的部分建議,因為書籍還沒有閱讀完,會慢慢的完善補充。

正確操作字串

1.1 確保儘量少的裝箱

在使用其他值引用型別到字串的轉換並完成拼接時,應當避免使用操作符“+”來完成,而應該使用值引用型別提供的ToString方法。

例如:

String str1=str1+9

String str2=str2+9.ToString();

1.2避免分配額外的記憶體空間

使用預設轉型方法

這些轉型方法包括:

使用型別的轉換運算子。隱式轉換與顯式轉換。

使用型別內建的ParseTryParse,或者如ToStringToDoubleToDateTime等方法。

使用幫助類提供的方法。如System.ConvertSystem.BitConverter等。

使用CLR支援的轉型。上溯轉型與下溯轉型。

區別對待強制轉型與asis

如果型別之間都上溯到了某個共同的基類,那麼根據此基類進行的轉型(即基類轉型為子類本身)應該使用as,子類與子類之間的轉型,則應該提供轉換操作符,以便進行強制轉型。

4 TryParseParse

兩者最大的區別是,如果字串格式不滿足轉換的要求,Parse方法將會引發一個異常;TryParse方法將不會引發異常,它會返回

false,同時將result置為0

使用int?來確保值型別也可以為null

T?是NullableT〉的縮寫,兩者可以相互轉換。T必須為結構體。

區別readonlyconst的使用方法

兩者的區別:

const是一個編譯器常量,readonly是一個執行時常量。

const只能修飾基元型別,列舉型別或字串型別,readonly沒有限制。

0值作為列舉的預設值

避免給列舉型別的元素提供顯式的值

習慣過載運算子

10 建立物件時需要考慮是否實現比較器

11 區別對待== 和 Equals

無論是操作符“==”還是方法“Equals”,都傾向於表達這樣一個原則:

對於值型別,如果型別的值相等,就應該返回True

對於引用型別,如果型別指向同一個物件,則返回True

FCL中,string的比較被過載為針對“型別的值”的比較,而不是針對“引用本身”的比較。

12 重寫Equals時也要重寫GetHashCode

13 為型別輸出格式化字串

IFormattable介面,IFormatProvider介面,ICustomFormatter介面的使用。

14 正確實現淺拷貝和深拷貝

淺拷貝:將物件中的所有欄位複製到新的物件(副本)中。其中,值型別欄位的值被複制到副本中後,在副本中的修改不會影響到源物件對應的值。而引用型別的欄位被複制到副本中的是引用型別的引用,而不是引用的物件,在副本中對引用型別的欄位值做修改會影響到源物件本身。

深拷貝:同樣,將物件中的所有欄位複製到新的物件(副本)中。不過,無論是物件的值型別欄位,還是引用型別欄位,都會被重新建立並賦值,對於副本的修改,不會影響到源物件本身。

15 使用Dynamic來簡化反射實現

16 元素數量可變的情況下不應使用陣列

17 多數情況下使用foreach進行迴圈遍歷

foreach優點:語法更簡化,預設呼叫Dispose方法。

18 foreach不能代替for

foreach的一個缺點:不支援迴圈時對集合進行增刪操作(因為版本檢測的緣故)。

19 使用更有效的物件和幾何初始化

物件和集合初始值設定項

20 使用泛型集合代替非泛型集合

21 選擇正確的集合

如果集合的資料固定並且不涉及轉型,使用陣列效率高,否則就使用List<T>。

22 確保集合的執行緒安全

集合執行緒安全是指在多個執行緒上新增或者刪除元素時,執行緒之間必須保持同步。

加鎖或者使用執行緒安全的集合類(位於System.Collections.Concurrent名稱空間下)。

23 避免將List<T>作為自定義集合類的基類

24 迭代器應該是隻讀的

25 謹慎集合屬性的可寫操作

如果型別的屬性中有集合屬性,那麼應該保證屬性物件是有型別本身產生的。

26 使用匿名型別儲存LINQ查詢結果

27 在查詢中使用Lambda表示式

28 區別LINQ查詢中德IEnumerable<T>與IQueryable<T>

30 使用LINQ取代集合中德比較器和迭代器

31在LINQ查詢中避免不必要的迭代

編碼過程中,要充分運用First和Take等方法。

32 總是優先考慮泛型

33 避免在泛型型別中宣告靜態成員

泛型型別之間不共享靜態成員;非泛型型別中的泛型方法並不會在執行時的原生代碼中生成不同的型別。

34 為泛型引數設定約束

35 使用default為泛型型別變數指定初始值

36 使用FCL中的委託型別

37 使用Lambda表示式代替方法和匿名方法

38 小心閉包中的陷阱

39 瞭解委託的實質

40 使用event關鍵字為委託施加保護

41 實現標準的事件模型

42 使用泛型引數相容泛型介面的不可變性

43 讓介面中的泛型引數支援協變

44 理解委託中的協變

45 為泛型型別引數指定逆變

46 顯式釋放資源需繼承介面IDisposable

47 即使提供了顯式釋放方法,也應該在終結器中提供隱式清理

~XXClass()

{Dispose(false);}

48 Dispose方法應允許被多次呼叫

49 在Dispose模式中應提取一個受保護的虛方法

存在派生類時,提醒派生類在自己的釋放方法中呼叫base.Dispose方法。

50 在Dispose模式中應區別對待託管資源和非託管資源

51 具有可釋放欄位的型別或擁有本機資源的型別應該是可釋放的

52 及時釋放資源

58 湧丟擲異常代替返回錯誤程式碼

59 不要在不恰當的場合下引發異常

正常的業務流程不應使用異常來處理。

不要總是嘗試去捕獲異常或引發異常,而應該允許異常向呼叫堆疊往上傳播。

應該引發異常的情況:

第一類情況 如果執行程式碼後會造成記憶體洩露,資源不可用,或者應用程式狀態不可恢復,則引發異常。

第二類情況 在捕獲異常的時候,如果需要包裝一些更有用的資訊,則引發異常。

第三類情況 如果底層異常在高層操作的上下文中沒有意義,則可以考慮捕獲這些底層異常,並引發新的有意義的異常。

60 重新引發異常時使用Inner Exception

61 避免在finally內撰寫無效程式碼

62 避免巢狀異常

直接throw e而不是throw將會重置堆疊資訊。

63 避免吃掉異常

如果你不知道如何處理某個異常,那麼千萬不要“吃掉”異常;如果異常可被預見,並且通常情況它不能算是一個Bug時,可以考慮“吃掉”異常。

64 為迴圈增加Tester-Doer模式而不是將try—catch置於迴圈內

因為迴圈中丟擲異常是一個相當影響效能的問題。

65 總是處理未捕獲的異常

90 不要為抽象類提供公開的構造方法

91 可見欄位應該重構為屬性

92 謹慎將陣列或集合作為屬性

93 構造方法應初始化主要屬性和欄位

94 區別對待override和new

95 避免在構造方法中呼叫虛成員

96 成員應優先考慮公開將基型別或介面

97 優先考慮將基型別或介面作為引數傳遞

98 用params減少重複引數

99 重寫時不應使用子類引數

100 靜態方和例項方法沒有區別

101 使用擴充套件方法,向現有型別“新增”方法

102 

122 以<Company>.<Component>為明明空間命名

123 程式集不必與名稱空間同名

124 考慮在名稱空間中使用複數

如System.Collections(複數形式),包含所有的非泛型集合類。

125 避免用FCL的型別名稱命名自己的型別

126 用名詞和名片語給型別命名

127 用形容片語給介面命名

如IDisposable,IEnumerable等,也有例外,如IEnumerator。

128 考慮讓派生類的名字以基類名字作為字首

如Exception類及其派生類。

129 泛型型別引數要以T作為字首

130 以複數命名列舉型別,以單數命名列舉元素

131 使用PascalCasing命名公開元素

132 考慮用類名作為屬性名

如果屬性對應一個型別,且僅有一個該型別的屬性,可以考慮用類名作為屬性名。

133 camelCasing命名私有欄位和區域性變數

134 有條件的使用字首

大型別(該類程式碼足夠長)中,使用字首來區分一個型別是例項變數(m_)還是靜態變數(s_)或者是一個const變數(名詞大寫加下劃線)。字首僅限於此。

135 考慮使用肯定性的短語命名布林屬性

IsXXHasXXCanXXAllowXX等。

136 優先使用字尾表示已有型別的新版本

X509CertificateX509Certificate2(替代版本)。

137 委託和事件型別應該新增上級字尾(參考建議128

如加上DelegateCallBack(回撥),EventHandler

138 事件和委託變數使用動詞或形容詞短語命名

139 事件處理器命名採用組合方式

事件處理器即實際被委託執行的那個方法。

事件處理器的命名規則:事件變數所屬物件+下劃線+事件變數名

為委託或委託中的回撥編寫處理器:委託變數所屬物件+On+委託變數名

140 使用預設的訪問修飾符

141 不知道該不該用大括號時,就用

142 總是提供有意義的命名

143 方法抽象級別應在同一層次

144 一個方法只做一件事

145 避免過長的方法和過長的類

146 只對外公佈必要的操作

147 重構多個相關屬性為一個類

型別中的相關屬性超過3個,就可以考慮將其重構為一個類。

148 不重複程式碼

保持程式碼的簡潔,避免Bug的出現。

149 使用表驅動法避免過長的ifswitch分支

例如按照索引值驅動的表驅動法。

150 使用匿名方法,Lambda表示式代替方法

151 使用事件訪問器替換公開的事件成員變數

類似於屬性之於公有欄位。

152 最少,甚至是不要註釋

153 若丟擲異常,則必須要註釋

154 不要過度設計,在敏捷中體會重構的樂趣

說實話,沒怎麼看,對敏捷不太感興趣。

155 隨生產程式碼一起提交單元測試程式碼

156 利用特性為應用程式提供多個版本

157 從寫第一個介面開始,就進行自動化測試