電腦科學 ^ IT人生 ^ 公益課程

#80 寫程式的參考準測 (coding guideline) - C# 篇

曾有一些朋友問我,在微軟公司裡是否有寫程式的準則 (coding guideline).這件事因不同的團隊而異,大部份的團隊都會依循 MSDN 文件裡的建議,但並非每一個團隊都有文件記錄這些準則.以前我在 Windows 部門裡的某個團隊就正好有文件說明 C# coding guideline.除了 C# coding guideline 以外,還有其他的文件,例如 code review 文件, database 開發文件等等.在這篇文章中,我將從 C# coding guideline 開始寫起.這些 coding guideline 不是什麼秘密,很多都是來自 MSDN 的文件.若你的團隊也需要一份 C# coding guideline, 希望能派的上用場.

1. 在一份 C# 原始碼裡,別有 Tab , space 並存.這一個可以善用 Visual Studio 的編輯器設定幫你解決.一般來說,我們用 space 來取代 tab,例如一個 tab 等於四個 spaces.

2. 一行程式碼的字數通常設定在 120 個字元左右.為了方便閱讀,若有一個 api 有多個參數需要傳入,將多個參數分行排列,如下例


3. 有關設定變數 (variable),盡可能地將其 scoping 範圍找小一點的.

4. 變數宣告時不一定要馬上給初始值 (initialize).用到此變數時在給即可.若需要給一個初始值,請給一個有意義的值做為初始值.這邊所謂有意義的初始值不代表該資料結構的預設值.如 int count = 0; ,這是多此一舉的行為.

5. Local variable 前面加上 _ .若是 global variable,則不用.
如 int _memberCount;
int MemberCount;

6. 相同型別的變數要分行宣告.別擠在一行一起宣告,應分行.如:
int foo;
int bar;
int baz;

7. 對於常數型的變數 (內容不會變動) 記得使用 const 宣告. 如:
private const int _defaultCount = 100;

另外,對於這類常數型變數,最好能加上一個 comment 用來說明為何選用此內容. 如:
// 100 is chosen because of the size limit of the queue
private const int _defaultCount = 100;

8. 對於多個性質相關的常數型變數,可以考慮將他們整合在一個 static class 裡宣告並使用,如:

可改成


9. 用 string.Empty 來取代 ""

10. 用 string.IsNullOrWhiteSpace() 來檢查字串是否為 null 或是空白.

11. 對於一些 “應用值”,不應該 “hard-code”,而是該透過其他較彈性的方式取得 (像 Configuration).如
網路連線重試次數,timeout 時間, thread 數量, 資料庫連線字串等等

12. 在 if , while , for, foreach, return 前多個空行,以便於閱讀.

13. Class 裡的 method 之間最好有一個或二個空白行.

14. 若有需要,善用 #region, #endregion 將相關的程式放在同一個區塊裡.同時 #region 之間也該有一個或二個空白行.

15. 善用 IDE 裡的編輯器幫助你處理好空白的事情.如


16. 遇到一些特別情況時,善用 comment 以便未來工作.寫 comment 時不用一行一行寫 (inline),請在適當地方用一塊區域來說明程式的運作目的.


17. 對於 public method 都該檢查輸入參數是否符合你的預期.如:


18. 該有括號的地方都打上去,別偷懶不打.如:


19. public method, property 等一律使用 PascalCasing ,而非 public 改用 camelCasing. 如:
public int GetMyReward();
public string MyName {get; set;}

private int getMyReward();
private string _myName;

20. 為 variable, property, method, class, interface 取名是件重要的事情.取一個完整的名稱,別用簡稱.如 使用 GetConsoleWindow() 而不用 GetConWin()

21. Namespace 命名以 PascalCasing 方式,名字應以名詞為主.如: System.Security

22. Class 命名以 PascalCasing 方式,名字應以名詞為主,如 StreamReader, DataCollector 等

23. Interface 命名以 PascalCasing 方式,通常在最前面加上 I ,名字應以名詞為主,如 IList, IReadOnlyCollection 等

24. Variable 與 parameter 命名以 camelCasing 方式,名字應以名詞為主,如:
public int ToInt32(string itemValue, int itemCount);

25. Method 命名以 PascalCasing 方式,名字應以動詞為主,如:
ToString(), Write(), ExecuteCommand();

26. Property 命名以 PascalCasing 方式,名字應以名詞為主,如:
public int Length { get; set; }

27. Event 命名以 PascalCasing 方式,名字應以動詞為主,如:
public event EventHandler ObjectChanged;

28. Public field 命名以 PascalCasing 方式,而 private field 以 camelCasing 方式,名字應以名詞為主,如:
public TimeSpan Timeout;
private TimeSpan _firstTimeout;

29. Enum 命名以 PascalCasing 方式,名字應以名詞為主,如:
FileMode
{
Open,
Create,
Append,
}

30. Class 的名字通常和對應的 Interface 名字有關係,如:


31. 大部份的情況下, Enum 最好要加上 None 元素用來代表該 enum 用不到的情況下,並且將它設定為 0.如:


32. Enum 若是 Flags 的屬性,別在名字加上 Flag,可用複數名詞來代表. 如


33. 為了便於閱讀和 code review,將關聯性高的 class 成員寫在相鄰的位置.

34. 一個檔案最好只有一個 class,並且檔案名字與 class 名字一致.若是 nested class 或是一些簡單且臨時在程式間用的 class 除外.

35. 所有的宣告都應加上 public, private, internal.若是只在一個 method 裡使用的臨時變數可以忽略.

36. 在 switch 裡要加上 default: ,即使它的內容是空的,也要加上.

37. 要產生準確的 exception.如,當你檢查參數是否為空時,就該用 ArgumentNullException 而不是用 ArgumentException.

38. 產生 exception 時,要產生有意義且可知道錯誤細節與解決方法的 exception,不該把許多可能會發生錯誤的 code 放在一個 try block 裡,然後只有一個 catch (Exception ex).

39. 當處理商業邏輯時,很可能找不到適合的 exception type,此時應自行建立自訂的 exception.

40. Exception 產生時不應該影響程式的正常流程,並且 exception 產生時需被處理,例如 log.

41. 在 try-catch 裡若使用到一些資源,可在 finally block 裡釋放.如:


42. 在你的程式裡,在最上層 (可能是在 UI 層) 要能抓住所有可能會發生的 exception,做好相對應的處理或畫面顯示,避免程式中斷.

43. 在 catch block 裡應該都為該 exception 做些處理動作,而不是空白 (什麼事都不做).除非,程式裡已有邏輯在處理那段錯誤,如:


44. 不用重覆拋出 exception.應該加一些錯誤處理機制並且只需要 throw 即可.如:


45. 對於有實做 IDisposable 的 class 應善用 using 來確保資源能適時適當地被釋放.如:


別在 try-finally 裡使用 Dispose,如:


46. 盡可能使用 generic collection 來儲存物件,例如使用 List<T> 而不用 ArrayList.

47. 所有的 public class, property, method 都應該要有 XML 文件註解,如:


48. 有適合的語法糖時就使用,以便於閱讀.如:


49. 在不影響程式邏輯的情況下,應把程式碼編排縮排量減少.如:


50. Public method 的參數應盡量使用 interface,如:


51. 若無特別需求,為每個非同步呼叫加上 ConfigureAwait(false)

52. 若無特別需求,不自行建立 Thead ,一律使用 Task 讓 .Net threadpool 來管理相關資源.

53. 多個 Task 有執行順序時,善用 ContinueWith(), WhenAll(), 或 WhenAny(),而不該用 Task.Delay.










Share:

0 意見:

張貼留言