讀書筆記之《編寫高質量程式碼:改善C#程式的157個建議》
最近,在閱讀書籍《編寫高質量程式碼:改善C#程式的157個建議》,感覺寫得很不錯,特將其中的建議整理了一下,待以後隨時檢視。
現只羅列了其中的部分建議,因為書籍還沒有閱讀完,會慢慢的完善補充。
1 正確操作字串
1.1 確保儘量少的裝箱
在使用其他值引用型別到字串的轉換並完成拼接時,應當避免使用操作符“+”來完成,而應該使用值引用型別提供的ToString方法。
例如:
String str1=“str1”+9;
String str2=“str2”+9.ToString();
1.2避免分配額外的記憶體空間
2 使用預設轉型方法
這些轉型方法包括:
使用型別的轉換運算子。隱式轉換與顯式轉換。
使用型別內建的Parse,TryParse,或者如ToString,ToDouble,ToDateTime等方法。
使用幫助類提供的方法。如System.Convert,System.BitConverter等。
使用CLR支援的轉型。上溯轉型與下溯轉型。
3 區別對待強制轉型與as和is
如果型別之間都上溯到了某個共同的基類,那麼根據此基類進行的轉型(即基類轉型為子類本身)應該使用as,子類與子類之間的轉型,則應該提供轉換操作符,以便進行強制轉型。
4 TryParse比Parse好
兩者最大的區別是,如果字串格式不滿足轉換的要求,Parse方法將會引發一個異常;TryParse方法將不會引發異常,它會返回
5 使用int?來確保值型別也可以為null
T?是Nullable〈T〉的縮寫,兩者可以相互轉換。T必須為結構體。
6 區別readonly與const的使用方法
兩者的區別:
const是一個編譯器常量,readonly是一個執行時常量。
const只能修飾基元型別,列舉型別或字串型別,readonly沒有限制。
7 將0值作為列舉的預設值
8 避免給列舉型別的元素提供顯式的值
9 習慣過載運算子
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 考慮使用肯定性的短語命名布林屬性
如IsXX,HasXX,CanXX,AllowXX等。
136 優先使用字尾表示已有型別的新版本
如X509Certificate和X509Certificate2(替代版本)。
137 委託和事件型別應該新增上級字尾(參考建議128)
如加上Delegate,CallBack(回撥),EventHandler。
138 事件和委託變數使用動詞或形容詞短語命名
139 事件處理器命名採用組合方式
事件處理器即實際被委託執行的那個方法。
事件處理器的命名規則:事件變數所屬物件+下劃線+事件變數名
為委託或委託中的回撥編寫處理器:委託變數所屬物件+On+委託變數名
140 使用預設的訪問修飾符
141 不知道該不該用大括號時,就用
142 總是提供有意義的命名
143 方法抽象級別應在同一層次
144 一個方法只做一件事
145 避免過長的方法和過長的類
146 只對外公佈必要的操作
147 重構多個相關屬性為一個類
型別中的相關屬性超過3個,就可以考慮將其重構為一個類。
148 不重複程式碼
保持程式碼的簡潔,避免Bug的出現。
149 使用表驅動法避免過長的if和switch分支
例如按照索引值驅動的表驅動法。
150 使用匿名方法,Lambda表示式代替方法
151 使用事件訪問器替換公開的事件成員變數
類似於屬性之於公有欄位。
152 最少,甚至是不要註釋
153 若丟擲異常,則必須要註釋
154 不要過度設計,在敏捷中體會重構的樂趣
說實話,沒怎麼看,對敏捷不太感興趣。
155 隨生產程式碼一起提交單元測試程式碼
156 利用特性為應用程式提供多個版本
157 從寫第一個介面開始,就進行自動化測試