[TOC] C 編程指南 前不久在 Github 上看見了一位大牛創建一個倉庫: "CSharpCodingGuidelines" ,打開之後看了一下 相關描述,感覺應該很不錯,於是就 clone 到本地拜讀一下,這裡列一些自己的筆記,方便日後回顧。 基本原則 Astonishment 原則:你的代 ...
目錄
C# 編程指南
前不久在 Github 上看見了一位大牛創建一個倉庫:CSharpCodingGuidelines,打開之後看了一下 readme.md
相關描述,感覺應該很不錯,於是就 clone 到本地拜讀一下,這裡列一些自己的筆記,方便日後回顧。
基本原則
- Astonishment 原則:你的代碼應該儘量做到讓每一個人都能理解。任何人都有寫出讓機器認識的代碼,但是並不是每個人都能寫出讓人認識的代碼;
- Kiss 原則:類似 Python 之禪 裡面說的那樣,簡單勝於複雜;
- YAGNI 原則:代碼儘量能做到可擴展,但請不要過度重構,因為你不能預知未來;
- DRY 原則:不要重覆造輪子,除非你有時間或者你造的輪子會比別人的優秀;
- 面向對象編程原則:繼承、封裝、多態、抽象;
類設計指南
- 一個類/介面應該只有一個用途,要符 合單一職責 原則;
- 只創建返回有用對象的構造函數,當構造函數的參數超過 3 的話,你就應該考慮你的類設計是否過於臃腫;
- 介面應該短小精悍,其定義要明確清晰地傳達出其具有的行為;
- 如果一種行為有多種實現,請考慮使用介面而不是基類;
- 儘量使用介面將類進行彼此解耦;
- 避免使用靜態類;
- 不要使用 new 關鍵字來禁止編譯器顯示相關警告;
public class Book
{
public virtual void Print()
{
Console.WriteLine("Printing Book");
}
}
public class PocketBook : Book
{
public new void Print()
{
Console.WriteLine("Printing PocketBook");
}
}
class Program
{
static void Main(string[] args)
{
PocketBook pocketBook = new PocketBook();
pocketBook.Print();
((Book)pocketBook).Print();
Console.ReadKey();
}
}
在上述代碼段中,我們創建一個基類 book,並定義了一個 Print() 方法,接著我們創建一個子類 PocketBook,並通過 new 關鍵字來重寫基類方法。在項目複雜的情況下,使用這種方式將導致我們不能準確預測是子類調用還是父類調用,使代碼複雜度提升。
- 應該可以將派生類當作基類對象來處理;
- 不要引用基類的派生類;
- 避免暴露一個對象依賴的其它對象;
- 避免雙向依賴;
- 類應該有狀態和行為;
- 類應該保護其內部狀態的一致性;
屬性成員設計指南
- 允許按任意順序設置屬性;
- 使用方法而不是屬性;
- 不要使用相互排斥的屬性;
- 屬性、方法或者本地方法只應該做一件事情;
- 不要通過靜態成員公開有狀態的對象;
- 用 IEnumerable
或者 ICollection 來代替具體的集合對象作為返回值; - 如果屬性、參數和返回值是字元串或者集合類型的話,則永遠不應該為空;
- 儘可能地定義具體的參數;
- 考慮使用特定域的值類型而不是基元;
其他設計指南
- 拋出異常而不是返回某種類型的狀態值;
- 提供完整而有意義的異常信息;
- 拋出適當的最具體的異常;
- 不要通過 try - catch 方式隱藏異常;
- 正確處理非同步代碼中的異常;
- 調用事件委托前先判斷是否為空;
event EventHandler<string> Notify;
protected virtual void OnNotify(string args)
{
Notify?.Invoke(this, args);
}
- 使用受保護的虛方法來觸發每個事件;
- 考慮添加屬性變化事件;
- 當觸發事件時要確保 sender != nulll;
- 如果合適的話,請考慮使用泛型約束;
class SomeClass
{
}
/// <summary>
/// 不推薦
/// </summary>
class MyClass1
{
void SomeMethod<T>(T t)
{
object temp = t;
SomeClass obj = (SomeClass)temp;
}
}
/// <summary>
/// 推薦
/// </summary>
class MyClass2
{
void SomeMethod<T>(T t) where T :SomeClass
{
SomeClass obj = t;
}
}
- 在返回 LINQ 表達式之前計算它的結果;
- 如果不是必須,不要使用 this 和 base 關鍵字;
可維護性指南
- 方法內部的代碼段儘量不要超過 7 行;
- 確保所有成員私有,類的類型預設為為 internal sealed
- 避免雙重條件;
- 在其包含的命名空間內命名程式集;
- 將源文件命名為它所包含的類型;
- 將源文件的內容限製為一種類型;
- 將不同的邏輯函數放到同一個部分類中;
- 在使用一個類型時,使用 using 關鍵字導入需要的命名空間,而不是類型的完整空間標識;
- 不要使用魔法數;
- 只有當類型顯而易見時才使用 var 關鍵字;
- 定義變數時儘可能地初始化;
- 在相互獨立的代碼段中定義臨時變數;
- 若對象有集合需要初始化的話在進行對象初始化的同時進行集合初始化;
- 不要顯式進行 bool 值的比較;
- 避免嵌套迴圈;
- 在使用 if、else、do、while、for、foreach、case 的同時使用
{}
; - 在 switch case 代碼段中添加 default 邏輯;
- 在所有的 if 、else if 後再添加 else;
- 避免使用多個返回值;
- 考慮使用簡單的條件語句代替 if else;
- 封裝屬性、方法或局部函數中的複雜表達式;
- 再合適的情況下嘗試重載方法;
- 使用可選參數來代替重載;
- 避免使用命名參數;
- 避免定義超過3個參數的簽名;
- 避免函數簽名為布爾類型;
- 不要將參數作為臨時變數使用;
- 將模式作為操作;
- 不要註釋代碼;
命名指南
- 不要在變數、參數和類型成員中包含數字;
- 不要在欄位添加首碼;
- 不要使用縮寫;
- 成員、參數和變數定義要根據它們代表的意義;
- 使用名詞、名詞短語或者形容詞來定義類型;
- 使用描述性名稱命名泛型參數;
- 在類成員中不要重覆定義和類相同的名稱;
- 成員定義可參考 .Net Framework 的定義方式;
- 避免使用可能被誤解的段名稱或欄位;
- 正確定義屬性;
- 在命名方法或局部函數時使用謂詞或謂詞對象;
- 使用名稱、層、謂詞和功能申明命名空間;
- 使用動詞或動詞首碼來定義事件;
- 使用 ing 和 end 尾碼來表達事件預處理和發送事件;
- 使用 on 首碼來定義事件處理程式;
- 使用 Async 或者 TaskAsync 來標識非同步方法;
性能指南
- 使用 Any() 判斷 IEnumerable
是否為空 ; - 僅對低密集型活動使用非同步;
- 對於 CPU密集型使用 Task.Run;
- 避免同時將 async/await 和 Task.Wait 混合使用;
- 避免 async/await 在單線程環境下出現死鎖;
框架指南
- 使用 C# 類型 別名而不是系量進行顯式調用;
- 不要硬編碼;統命名空間中的類型;
- 盡
- 使用最高警告級別編譯代碼;
- 對於簡單的表達式避免使用 LINQ;
- 使用 lambda 表達式來代替匿名函數;
- 只用在使用動態對象時才使用 dynamic 關鍵字;
- 支持非同步/等待任務延續;
文檔指南
- 使用美式英語來編寫相關文檔;
- 文檔中的代碼部分要保證完整性;
- 與其他開發人員一起編寫 xml 文檔;
- 編寫 MSDN 風格的技術文檔;
- 避免內聯註釋;
- 註釋值應該用來解釋複雜的演算法或討論;
- 不要使用註釋來跟蹤要在以後完成的工作;
佈局指南
- 使用常規佈局;
- 根據公式要求進行命名空間的約束;
- 將成員置於定義良好的順序中;
- 謹慎使用
#region
; - 適當使用表現健全的成員;
相關鏈接
- Code Complete: A Praccal Handbook of Soware Construcon (Steve McConnel)
- The Art of Agile Development (James Shore)
- Applying Domain-Driven Design and Paerns: With Examples in C# and .NET (Jimmy Nilsson)
- Jeremy D. Miller's Blog
- LINQ Framework Design Guidelines
- Best Pracces for c# async/await