|
陳年的文檔,不過很不錯整理出來發(fā)到博客上,還有更多的朋友需要。 10.5.1. 參數(shù)設計中枚舉和布爾參數(shù)的選擇規(guī)范 21 12.3.1. Exception與SystemException 23 12.3.2. InvalidOperationException 24 12.3.3. ArgumentException,ArgumentNullException,ArgumentOutOfRangeException 24 12.3.4. NullRefernceException,IndexOutOfRangeException,AccessViolationException 24 12.3.5. StackOverflowException: 24 12.3.6. OutOfMemoryException: 24
1. 簡介本規(guī)范為一套編寫高效可靠的 C# 代碼的標準、約定和指南。它以安全可靠的軟件工程原則為基礎,使代碼易于理解、維護和增強,提高生產效率。同時,將帶來更大的一致性,使軟件開發(fā)團隊的效率明顯提高。 2. 適用范圍本規(guī)范適用于公司所有的C#源代碼,為詳細設計,代碼編寫和代碼審核提供參考和依據(jù)。 3. 文體本規(guī)范中的建議分為四種:要,建議,避免,不要,表示需要遵循的級別。文檔中會以粗體表示。對于應遵循的規(guī)范,前面會以“?”來表示,對不好的做法前面會以“′”來表示: 要:描述必須遵循的規(guī)范。例如: 異常類要以“Exception”做為后綴; 建議:描述在一般情況下應該遵循的規(guī)范,但如果完全理解規(guī)范背后的道理,并有很好的理由不遵循它時,也不畏懼打破常規(guī)。例如: 強制類型轉換時,在類型和變量之間建議加一空格。 不要:描述一些幾乎絕對絕不應該違反的規(guī)范。例如: ′ 每個函數(shù)有效代碼(不包括注釋和空行)長度不要超過50行。 避免:與建議相對,一般情況下應該遵循,但有很好的理由時也可以打破。例如: ′ 避免塊內部的變量與它外部的變量名相同。 對一些規(guī)范內容一并提供了示例代碼。 4. 代碼組織與風格4.1. Tab要使一個Tab為4個空格長。 4.2. 縮進要使一個代碼塊內的代碼都統(tǒng)一縮進一個Tab長度。 4.3. 空行建議適當?shù)脑黾涌招?,來增加代碼的可讀性。 在在類,接口以及彼此之間要有兩行空行: 在下列情況之間要有一行空行: 方法之間; 局部變量和它后邊的語句之間; 方法內的功能邏輯部分之間; 4.4. 函數(shù)長度′ 每個函數(shù)有效代碼(不包括注釋和空行)長度不要超過50行。 4.5. {”,“}”開括號“{”要放在塊的所有者的下一行,單起一行; 閉括號“}”要單獨放在代碼塊的最后一行,單起一行。 4.6. 行寬′ 每行代碼和注釋不要超過70個字符或屏幕的寬度,如超過則應換行,換行后的代碼應該縮進一個Tab。 4.7. 空格′ 括號和它里面的字符之間不要出現(xiàn)空格。括號應該和它前邊的關鍵詞留有空格,如:while (true) {}; ′ 但是方法名和左括號之間不要有空格。 參數(shù)之間的逗號后要加一空格。如:method1(int i1, int i2) for語句里的表達式之間要加一空格。如:for (expr1; expr2; expr3) 二元操作符和操作數(shù)之間要用空格隔開。如:i + c; 強制類型轉換時,在類型和變量之間要加一空格。如:(int) i ; 5. 注釋5.1. 注釋的基本約定注釋應該增加代碼的清晰度; 保持注釋的簡潔,不是任何代碼都需要注釋的,過多的注釋反而會影響代碼的可讀性。 ′ 注釋不要包括其他的特殊字符。 建議先寫注釋,后寫代碼,注釋和代碼一起完成 如果語句塊(比如循環(huán)和條件分枝的代碼塊)代碼太長,嵌套太多,則在其結束“}”要加上注釋,標志對應的開始語句。如果分支條件邏輯比較復雜,也要加上注釋。 在VS2005環(huán)境中通過配置工程編譯時輸出XML文檔文件可以檢查注釋的完整情況,如果注釋不完整會報告編譯警告; 5.2. 注釋類型5.2.1. 塊注釋主要用來描述文件,類,方法,算法等,放在所描述對象的前邊。具體格式以IDE編輯器輸入“///”自動生成的格式為準,另外再附加我們自定義的格式,如下所列: /// <Remark>作者,創(chuàng)建日期,修改日期</ Remark > 對類和接口的注釋必須加上上述標記,對方法可以視情況考慮 5.2.2. 行注釋主要用在方法內部,對代碼,變量,流程等進行說明。整個注釋占據(jù)一行。 5.2.3. 尾隨注釋與行注釋功能相似,放在代碼的同行,但是要與代碼之間有足夠的空間,便于分清。例: int m = 4 ; // 注釋 如果一個程序塊內有多個尾隨注釋,每個注釋的縮進要保持一致。 5.3. 注釋哪些部分
5.4. 程序修改注釋新增代碼行的前后要有注釋行說明,對具體格式不作要求,但必須包含作者,新增時間,新增目的。在新增代碼的最后必須加上結束標志; 刪除代碼行的前后要用注釋行說明,刪除代碼用注釋原有代碼的方法。注釋方法和內容同新增;刪除的代碼行建議用#region XXX #endregion 代碼段折疊,保持代碼文件干凈整潔 修改代碼行建議以刪除代碼行后再新增代碼行的方式進行(針對別人的代碼進行修改時,必須標明,對于自己的代碼進行修改時,酌情進行)。注釋方法和內容同新增; 6. 命名6.1. 命名的基本約定要使用可以準確說明變量/字段/類的完整的英文描述符,如firstName。對一些作用顯而易見的變量可以采用簡單的命名,如在循環(huán)里的遞增(減)變量就可以被命名為 ” i ”。 要盡量采用項目所涉及領域的術語。 要采用大小寫混合,提高名字的可讀性。為區(qū)分一個標識符中的多個單詞,把標識符中的每個單詞的首字母大寫。不采用下劃線作分隔字符的寫法。有兩種適合的書寫方法,適應于不同類型的標識符: PasalCasing:標識符的第一個單詞的字母大寫; camelCasing:標識符的第一個單詞的字母小寫。 下表描述了不同類型標識符的大小寫規(guī)則:
′ 避免使用縮寫,如果一定要使用,就謹慎使用。同時,應該保留一個標準縮寫的列表,并且在使用時保持一致。 對常見縮略詞,兩個字母的縮寫要采用統(tǒng)一大小寫的方式(示例:ioStream,getIOStream);多字母縮寫采用首字母大寫,其他字母小寫的方式(示例:getHtmlTag); ′ 避免使用長名字(最好不超過 15 個字母)。 ′ 避免使用相似或者僅在大小寫上有區(qū)別的名字。 6.2. 各種標示符類型的命名約定6.2.1. 程序集命名公司域名(Techstar)+ 項目名稱 + 模塊名稱(可選),例如: 中心系統(tǒng)程序集:Techstar.ProductionCenter; 中心系統(tǒng)業(yè)務邏輯程序集:Techstar. ProductionCenter.Business; 6.2.2. 命名空間命名采用和程序集命名相同的方式:公司域名(Techstar)+ 項目名稱 + 模塊名稱。 另外,一般情況下建議命名空間和目錄結構相同。例如: 中心系統(tǒng):Techstar.ProductionCenter; 中心系統(tǒng)下的用戶控件:Techstar.ProductionCenter.UserControl; 中心系統(tǒng)業(yè)務邏輯:Techstar. ProductionCenter.Business; 中心系統(tǒng)數(shù)據(jù)訪問:Techstar. ProductionCenter.Data; 6.2.3. 類和接口命名類的名字要用名詞; ′ 避免使用單詞的縮寫,除非它的縮寫已經(jīng)廣為人知,如HTTP。 接口的名字要以字母I開頭。保證對接口的標準實現(xiàn)名字只相差一個“I”前綴,例如對IComponent的標準實現(xiàn)為Component; 泛型類型參數(shù)的命名:命名要為T或者以T開頭的描述性名字,例如: public class List<T> public class MyClass<TSession> ′ 對同一項目的不同命名空間中的類,命名避免重復。避免引用時的沖突和混淆; 6.2.4. 方法命名第一個單詞一般是動詞 如果方法返回一個成員變量的值,方法名一般為Get+成員變量名,如若返回的值 是bool變量,一般以Is作為前綴。另外,如果必要,考慮用屬性來替代方法,具 體建議見10.1.2節(jié); 如果方法修改一個成員變量的值,方法名一般為:Set + 成員變量名。同上,考慮 用屬性來替代方法; 6.2.5. 變量命名按照使用范圍來分,我們代碼中的變量的基本上有以下幾種類型,類的公有變量;類的私有變量(受保護同公有);方法的參數(shù)變量;方法內部使用的局部變量。這些變量的命名規(guī)則基本相同,見標識符大小寫對照表。區(qū)別如下: i. 類的公有變量按通常的方式命名,無特殊要求; ii. 類的私有變量采用兩種方式均可:采用加“m”前綴,例如mWorkerName; iii. 方法的參數(shù)變量采用camalString,例如workerName; iv. 方法內部的局部變量采用camalString,例如workerName; ′ 不要用_或&作為第一個字母; 盡量要使用短而且具有意義的單詞; 單字符的變量名一般只用于生命期非常短暫的變量。i,j,k,m,n一般用于integer;c,d,e 一般用于characters;s用于string 如果變量是集合,則變量名要用復數(shù)。例如表格的行數(shù),命名應為:RowsCount; 命名組件要采用匈牙利命名法,所有前綴均應遵循同一個組件名稱縮寫列表 6.3. 組件名稱縮寫列表縮寫的基本原則是取組件類名各單詞的第一個字母,如果只有一個單詞,則去掉其中的元音,留下輔音??s寫全部為小寫。
7. 聲明每行要只有一個聲明,如果是聲明i,j,k之類的簡單變量可以放在一行; 除了for循環(huán)外,聲明要放在塊的最開始部分。for循環(huán)中的變量聲明可以放在for語句中。如:for(int i = 0; I < 10; i++) 。 ′ 避免塊內部的變量與它外部的變量名相同。 8. 表達式和語句每行建議只有一條語句。 if-else,if-elseif語句,任何情況下,都應該有“{”,“}”,格式如下: if (condition) { statements; } else if (condition) { statements; } else { statements; } for語句格式如下: for (initialization; condition; update) { statements; } 如果語句為空: for (initialization; condition; update) ; while語句格式如下: while (condition) { statements; } 如果語句為空: while (condition); do-while語句格式如下: do { statements; } while (condition); switch語句,每個switch里都應包含default子語句,格式如下: switch (condition) { case ABC: statements; /* falls through */ case DEF: statements; break; case XYZ: statements; break; default: statements; break; } try-catch語句格式如下: try { statements; } catch (ExceptionClass e) { statements; } finally { statements; } 9. 類型設計規(guī)范要確保每個類型由一組定義明確,相互關聯(lián)的成員組成,而不僅僅是一些無關功能的隨 機集合; 9.1. 類型和命名空間要用命名空間把類型組織成相關域的層次結構。例如: 界面層:Techstar.ProductionCenter; 業(yè)務邏輯層:Techstar.ProductionCenter.Business; 數(shù)據(jù)訪問層:Techstar.ProductionCenter.Data; ′ 避免過深的命名空間; ′ 避免太多的命名空間; 9.2. 類型和接口的選擇要優(yōu)先采用類而不是接口。 接口的缺點在于語義變化時改變困難。注意接口并不是協(xié)定,把協(xié)定和實現(xiàn)分開并非一 定用接口實現(xiàn),用基類和抽象類同樣可以表達; 建議使用抽象類而不是接口來解除協(xié)定與實現(xiàn)間的偶合; 要定義接口,來實現(xiàn)類似多重繼承的效果; 精心定義接口的標志是一個接口只做一件事情。關鍵是接口的協(xié)定需要保持不變, 如果一個接口包含太多功能,那么這個胖接口產生變化的機會就會大得多。 9.3. 抽象類設計:′ 不要在抽象類中定義公有的或內部受保護的構造函數(shù)。因為抽象類無法實例化,所以這 種設計會誤導用戶; 要為抽象類定義受保護的構造函數(shù)或內部構造函數(shù); 9.4. 靜態(tài)類設計靜態(tài)類是一個只包含靜態(tài)成員的類,它提供了一種純面向對象設計和簡單性之間的一個權衡,廣泛用來提供類似于全局變量或一些通用功能。 要少用靜態(tài)類。靜態(tài)類應該僅用作輔助類; ′ 避免把靜態(tài)類當作雜物箱。每個靜態(tài)類都應該有其明確目的; 不要在靜態(tài)類中聲明或覆蓋實例成員; 9.5. 枚舉設計要用枚舉來加強那些表示值的集合的參數(shù),屬性以及返回值的類型性; 要優(yōu)先使用枚舉而不是靜態(tài)常量。例如: //不好的寫法 public static class Color { public static int Red = 0; public static int Green = 1; public static int Blue = 2; } //好的寫法 public enum Color { Red, Green, Blue } ′ 不要把枚舉用于開放的場合,例如操作系統(tǒng)的版本,朋友的名字等; ′ 枚舉最后一個值不要加逗號; ′ 枚舉中不要提供為了今后使用而保留的枚舉值; 10. 成員設計規(guī)范方法,屬性,事件,構造函數(shù)以及字段等統(tǒng)稱為成員。 10.1. 成員設計的一般規(guī)范10.2. 方法的重載規(guī)范;′ 避免在重載中隨意的給參數(shù)命名。如果兩個重載中的某個參數(shù)表示相同的輸入,那么該參數(shù)的名字應該相同。例如: public class String { //好的寫法 public int IndexOf(string value) { ...} public int IndexOf(string value, int startIndex) { ...} //不好的寫法 public int IndexOf(string value) { ...} public int IndexOf(string str, int startIndex) { ...} } ′ 避免使重載成員的參數(shù)順序不一致。在所有的重載中,同名參數(shù)應該出現(xiàn)在相同的位置。 例如: public class EventLog { public EventLog(); public EventLog(string logName); public EventLog(string logName, string machineName); public EventLog(string logName, string machineName, string source); } 較短的重載應該僅僅調用較長的來實現(xiàn)。另外,重載如果需要擴展性,把最長重載 做成虛函數(shù)。例如: public class String { public int IndexOf(string s) { //調用 return IndexOf(s, 0); } public int IndexOf(string s, int startIndex) { //調用 return IndexOf(s, startIndex, s.Length); } public virtual int IndexOf(string s, int startIndex, int Count) { //實際的代碼 } } 要允許可選參選為null。這樣做是為了避免調用者調用之前需要檢查參數(shù)是否null。例 如: //允許為null時的調用 DrawGeometry(brush, pen, geometry); //不允許為null時的調用 if (geometry == null) DrawGeometry(brush, pen); else DrawGeometry(brush, pen, geometry); 10.3. 屬性和方法的選擇基本原則是方法表示操作,屬性表示數(shù)據(jù)。如果其他各方面都一樣,優(yōu)先使用屬性而不 是方法。 要使用屬性,如果該成員表示類型的邏輯attribue 如果屬性的值存儲在內存中,而提供屬性的目的僅僅是為了訪問該值,要使用屬性而不 要使用方法 如果該操作每次返回的結果不同,那么要使用方法。例如來自于.net framework的例子: //好的寫法 Guid.NewGuid(); //不好的寫法 DateTime.Now; 如果該操作比訪問字段慢一個或多個數(shù)量級,要使用方法。 如果該操作有嚴重的副作用,要使用方法。 10.4. 屬性的設計規(guī)范:如果不應該讓調用方法改變屬性值,要創(chuàng)建只讀屬性; ′ 不要提供只寫屬性; 要為所有的屬性提供合理的默認值,這樣可以確保默認值不會導致漏洞或效率低的代 碼; 要允許用戶以任何順序來設置屬性的值; 避免在屬性的獲取方法拋出異常。 屬性的獲取方法應該是個簡單的操作,不應該有任何的條件。如果一個獲取方法會拋出 異常,按么可能它更應該設計為方法。 10.5. 構造函數(shù)的設計規(guī)范建議提供簡單的構造函數(shù),最好是默認構造函數(shù)。簡單的構造函數(shù)增強易用性; 考慮擴展性,如果構造函數(shù)設計的不自然,建議用靜態(tài)的工廠方法來替代構造函數(shù); 要把構造函數(shù)的參數(shù)用作設置主要屬性的便捷方法。如果構造函數(shù)參數(shù)僅用來設置屬 性,應和屬性名稱相同。僅有大小寫的區(qū)別; 要在構造函數(shù)中做最少的工作。任何其他處理應該推遲到需要的時候; 要在類中顯示的聲明公用的默認構造函數(shù),如果這樣的構造函數(shù)是必須的。 如果沒有顯示默認構造函數(shù),填加有參數(shù)構造函數(shù)時往往會破壞已有使用默認構造函數(shù) 的代碼; ′ 避免在對象的構造函數(shù)內部調用虛成員。這樣在擴展設計的時候會導致難以理解的現(xiàn) 象; 10.6. 字段設計規(guī)范′ 不要提供公有的或受保護的字段。代之以屬性來訪問字段; 要只用常量字段來表示永遠不會改變的量。否則會導致兼容性問題。下面是正確的例子: public struct Int32 { public const int MaxValue = 0x7fffffff; public const int MinValue = unchecked((int)0x80000000); } 要用公有的靜態(tài)只讀字段來定義預定義的對象實例。例如: public struct Color { public static readonly Color Red = new Color(0x0000FF); } 10.7. 參數(shù)的設計規(guī)范要用類結構層次中最接近基類類型來作為參數(shù)的類型,同時要保證該類型能夠提供成員 所需的功能。例如: 要設計一個集合遍歷的方法,那么參數(shù)應該是IEnbumerable為參數(shù),而不應該是IList, 這樣方法具有更強的適應性。 ′ 不要使用保留參數(shù)。如果將來需要更多的參數(shù),那么可以增加重載成員。例如: //不好的寫法 public void Method(string reserved, SomeOption option); //好的寫法 public void Method(SomeOption option); //將來填加 public void Method(SomeOption option, string path); 10.7.1. 參數(shù)設計中枚舉和布爾參數(shù)的選擇規(guī)范要用枚舉。在代碼閱讀,書寫中,枚舉都比布爾的可讀性好很多。例如: //使用布爾型,閱讀的時候不會輕易了解參數(shù)的含義 FileStream f = File.Open(“1.txt”, true, false); //使用枚舉型 FileStream f = File.Open(“1.txt”,CasingOptions.CaseSenstive, FileMode.Open); ′ 不要使用布爾參數(shù),除非百分之百肯定絕對不需要兩個以上的值。即使此時,采用枚舉 往往也可以提供更好的可讀性,如上例。 考慮在構造函數(shù)中,對確實只有兩種狀態(tài)值的參數(shù)以及用來初始化布爾屬性的參數(shù)使用 布爾類型; 10.7.2. 參數(shù)驗證的規(guī)范:要驗證傳給公有的,受保護的或顯示成員的參數(shù)是否合法。如果驗證失敗,應該拋出 System.ArgutmentException或其子類; 要拋出System.ArgutmentNullException,如果傳入的null,而該成員不支持null; 10.7.3. 參數(shù)傳遞的規(guī)范:′ 避免使用輸出參數(shù)或引用參數(shù); 11. 擴展性設計規(guī)范′ 如果沒有恰當理由,不要把類密封起來。這些理由包括: A)類為靜態(tài)類; B)類的受保護成員保存了高度機密信息; C)類繼承了許多虛成員,逐個密封的代價太高,不如密封整個類; D)不要在密封類中聲明保護成員或虛成員,因為無法覆蓋其實現(xiàn); 建議用保護成員用于高級定制。它提供了擴展性,同時也避免了公用接口過于復雜; ′ 不要使用虛成員,除非有合適的理由; 建議只有在絕對必須的時候才用虛成員提供擴展性,并使用Template Method模式; 要優(yōu)先使用受保護的虛成員,而不是公有虛成員。公有成員通用調用受保護的虛成員的方式來提供擴展性; 12. 異常處理規(guī)范異常的思想是只對錯誤采用異常處理:邏輯和編程錯誤,設置錯誤,被破壞的數(shù)據(jù),資源耗盡,等等。通常的法則是系統(tǒng)在正常狀態(tài)下以及無重載和硬件失效狀態(tài)下,不應產生任何異常。異常處理時可以采用適當?shù)娜罩緳C制來報告異常,包括異常發(fā)生的時刻; ′ 一般情況下不要使用異常實現(xiàn)來控制程序流程結構; ′ 使用異常而不要用錯誤代碼來報告錯誤; 要通過拋出異常的方式來報告操作失敗。如果成員無法成功地完成它應該做的任務,那么應該拋出異常; 12.1. 異常類型選擇規(guī)范優(yōu)先考慮使用System命名空間中已有的異常,而不是自己創(chuàng)建新的異常類型; 要使用最合理,最具針對性的異常。例如,對參數(shù)為空,應拋出 System.ArgutmentNullException,而不是System.ArgutmentException 12.2. 異常處理規(guī)范′ 不是百分之百確定的情況,不要吞掉異常; 建議捕獲特定類型的異常,如果理解該異常在具體環(huán)境當中產生的原因; ′ 不要捕獲不應該捕獲的異常,通常應該允許異常沿著調用棧傳遞; 進行清理工作時要用try-finally,避免使用try-catch; 要在捕獲并重新拋出異常時使用空的throw語句,這是保持調用棧的最好方法 12.3. 標準異常類的使用:12.3.1. Exception與SystemException′ 不要拋出這兩種類型的異常; ′ 避免捕獲這兩種異常,除非是在頂層的異常處理器中; 12.3.2. InvalidOperationException對象處于不正確狀態(tài)時拋出; 12.3.3. ArgumentException,ArgumentNullException,ArgumentOutOfRangeException如果傳入的是無效參數(shù),要拋出參數(shù)異常,盡可能使用位于繼承層次末尾的類型; 要在拋出異常時設置ParaName屬性; 12.3.4. NullRefernceException,IndexOutOfRangeException,AccessViolationException′ 不要顯示拋出或捕獲; 12.3.5. StackOverflowException:′ 不要顯示拋出或捕獲; 12.3.6. OutOfMemoryException:′ 不要顯示拋出或捕獲; 12.4. 自定義異常類型設計規(guī)則:′ 避免太深的繼承層次; 要從已有的異?;惱^承; 異常類要以“Exception”做為后綴; 要使異??尚蛄谢蛊淠芸鐟贸绦蛴蚝瓦h程邊界仍能正常使用; 要把與安全性有關的信息保存在私有的異常狀態(tài)中 12.5. 異常與性能如果在普通場景都會拋出異常,要采用先效驗合法性的方式來避免拋出異常引起的性能 問題; 13. 其他規(guī)定為避免頻繁改動代碼,代碼中只寫比較簡單的和不會經(jīng)常發(fā)生變化的SQL,如果SQL 經(jīng)常發(fā)生變化或是比較復雜,存到SysMisc中,比如統(tǒng)計用到的SQL; 在VS2005開發(fā)環(huán)境中,采用代碼分析工具來做自動化的代碼分析,以保證代碼質量, 具體的使用建議如下: A)啟用代碼分析,并設置當風格不符合要求時為錯誤而不是警告; B)如果不是做代碼審核,此開關應關閉。加上了這個選項的時候編譯很慢; C)詳設的時候打開開關,檢查詳設是否符合編程規(guī)范; D)所有的選項都應當打開。以下內容需要單獨設置:
14. 參考文檔1,《.NET設計規(guī)范》,本規(guī)范很多內容都參考了這本書,書中對規(guī)范背后的背景和原則做了深入討論; <逆水行舟,不進則退> |
|
|