1. 程式人生 > >c#代碼規範

c#代碼規範

分隔 ane val enum 操作符 意義 重復 可選 cells

1.1.Tab

? 使一個Tab為4個空格長。

1.2.縮進

? 使一個代碼塊內的代碼都統一縮進一個Tab長度。

1.3.空行

? 建議適當的增加空行,來增加代碼的可讀性。

? 在在類,接口以及彼此之間有兩行空行:

? 在下列情況之間有一行空行:

方法之間;

局部變量和它後邊的語句之間;

方法內的功能邏輯部分之間;

1.4.函數長度

每個函數有效代碼(不包括註釋和空行)長度不要超過100行,盡可能控制在50行。

1.5.{”,“}”

? 開括號“{”放在塊的所有者的下一行,單起一行;

? 閉括號“}”單獨放在代碼塊的最後一行,單起一行。

1.6.行寬

每行代碼和註釋不要超過70個字符或屏幕的寬度,如超過則應換行,換行後的代碼應該縮進一個Tab。

1.7. 空格

′ 括號和它裏面的字符之間不要出現空格。括號應該和它前邊的關鍵詞留有空格,如:while (true) {};

′ 但是方法名和左括號之間不要有空格。

? 參數之間的逗號後加一空格。如:method1(int i1, int i2)

? for語句裏的表達式之間加一空格。如:for (expr1; expr2; expr3)

? 二元操作符和操作數之間用空格隔開。如:i + c;

? 強制類型轉換時,在類型和變量之間加一空格。如:(int) i ;

2. 註釋

2.1.註釋的基本約定

? 註釋應該增加代碼的清晰度;

? 保持註釋的簡潔,不是任何代碼都需要註釋的,過多的註釋反而會影響代碼的可讀性。

′ 註釋不要包括其他的特殊字符。

? 建議先寫註釋,後寫代碼,註釋和代碼一起完成

? 如果語句塊(比如循環和條件分枝的代碼塊)代碼太長,嵌套太多,則在其結束“}”加上註釋,標誌對應的開始語句。如果分支條件邏輯比較復雜,也加上註釋。

? 在VS2005環境中通過配置工程編譯時輸出XML文檔文件可以檢查註釋的完整情況,如果註釋不完整會報告編譯警告;

2.2.註釋類型

2.2.1.塊註釋

? 主要用來描述文件,類,方法,算法等,放在所描述對象的前邊。具體格式以IDE編輯器輸入“///”自動生成的格式為準,另外再附加我們自定義的格式,如下所列:

/// <Remark>作者,創建日期,修改日期</ Remark >

對類和接口的註釋必須加上上述標記,對方法可以視情況考慮

2.2.2.行註釋

? 主要用在方法內部,對代碼,變量,流程等進行說明。整個註釋占據一行。

2.2.3.尾隨註釋

? 與行註釋功能相似,放在代碼的同行,但是要與代碼之間有足夠的空間,便於分清。例:

int m = 4 ; // 註釋

? 如果一個程序塊內有多個尾隨註釋,每個註釋的縮進保持一致。

2.3.註釋哪些部分

項目

註釋哪些部分

參數

參數用來做什麽

任何約束或前提條件

字段/屬性

字段描述

類的目的

已知的問題

類的開發/維護歷史

接口

目的

它應如何被使用以及如何不被使用

局部變量

用處/目的

成員函數註釋

成員函數做什麽以及它為什麽做這個

哪些參數必須傳遞給一個成員函數

成員函數返回什麽

已知的問題

任何由某個成員函數拋出的異常

成員函數是如何改變對象的

包含任何修改代碼的歷史

如何在適當情況下調用成員函數的例子適用的前提條件和後置條件

成員函數內部註釋

控制結構

代碼做了些什麽以及為什麽這樣做

局部變量

難或復雜的代碼

處理順序

事件成員

事件用途

事件可能引發的異常

事件的參數含義

? 不是所有的成員函數都必須要註釋,以下功能函數可以不加註釋:

界面模塊的功能按鈕事件函數

override的函數

private的函數可以選擇性的加入註釋,如果private函數的功能十分簡單,可以不加入註釋,否則建議加入註釋

? private的字段/屬性可以不加註釋

? 局部變量只需要用”//”說明即可

2.4.程序修改註釋

? 新增代碼行的前後有註釋行說明,對具體格式不作要求,但必須包含作者,新增時間,新增目的。在新增代碼的最後必須加上結束標誌;

? 刪除代碼行的前後用註釋行說明,刪除代碼用註釋原有代碼的方法。註釋方法和內容同新增;刪除的代碼行建議用#region XXX #endregion 代碼段折疊,保持代碼文件幹凈整潔

? 修改代碼行建議以刪除代碼行後再新增代碼行的方式進行(針對別人的代碼進行修改時,必須標明,對於自己的代碼進行修改時,酌情進行)。註釋方法和內容同新增;

3. 命名

3.1.命名的基本約定

? 使用可以準確說明變量/字段/類的完整的英文描述符,如firstName。對一些作用顯而易見的變量可以采用簡單的命名,如在循環裏的遞增(減)變量就可以被命名為 ” i ”。

? 盡量采用項目所涉及領域的術語。

? 采用大小寫混合,提高名字的可讀性。為區分一個標識符中的多個單詞,把標識符中的每個單詞的首字母大寫。不采用下劃線作分隔字符的寫法。有兩種適合的書寫方法,適應於不同類型的標識符:

PasalCasing:標識符的第一個單詞的字母大寫;

camelCasing:標識符的第一個單詞的字母小寫。

下表描述了不同類型標識符的大小寫規則:

標識符

大小寫

示例

命名空間

Pascal

namespace Com.Techstar.ProductionCenter

類型

Pascal

public class DevsList

接口

Pascal

public interface ITableModel

方法

Pascal

public void UpdateData()

屬性

Pascal

Public int Length{…}

事件

Pascal

public event EventHandler Changed;

私有字段

Camel

private string fieldName;

非私有字段

Pascal

public string FieldName;

枚舉值

Pascal

FileMode{Append}

參數

Camel

public void UpdateData(string fieldName)

局部變量

Camel

string fieldName;

避免使用縮寫,如果一定要使用,就謹慎使用。同時,應該保留一個標準縮寫的列表,並且在使用時保持一致。

? 對常見縮略詞,兩個字母的縮寫采用統一大小寫的方式(示例:ioStream,getIOStream);多字母縮寫采用首字母大寫,其他字母小寫的方式(示例:getHtmlTag);

避免使用長名字(最好不超過 15 個字母)。

避免使用相似或者僅在大小寫上有區別的名字。

3.2.各種標示符類型的命名約定

3.2.1.程序集命名

? 公司域名(GTA)+ 項目名稱 + 模塊名稱(可選),例如:

中心系統程序集:GTA.ProductionCenter;

中心系統業務邏輯程序集:GTA. ProductionCenter.Business;

3.2.2.命名空間命名

? 采用和程序集命名相同的方式:公司域名(GTA)+ 項目名稱 + 模塊名稱。 另外,一般情況下建議命名空間和目錄結構相同。例如:

中心系統:GTA.ProductionCenter;

中心系統下的用戶控件:GTA.ProductionCenter.UserControl;

中心系統業務邏輯:GTA. ProductionCenter.Business;

中心系統數據訪問:GTA. ProductionCenter.Data;

3.2.3.類和接口命名

? 類的名字用名詞;

′ 避免使用單詞的縮寫,除非它的縮寫已經廣為人知,如HTTP。

? 接口的名字以字母I開頭。保證對接口的標準實現名字只相差一個“I”前綴,例如對IComponent的標準實現為Component;

? 泛型類型參數的命名:命名為T或者以T開頭的描述性名字,例如:

public class List<T>

public class MyClass<TSession>

′ 對同一項目的不同命名空間中的類,命名避免重復。避免引用時的沖突和混淆;

3.2.4.方法命名

? 第一個單詞一般是動詞

? 如果方法返回一個成員變量的值,方法名一般為Get+成員變量名,如若返回的值 是bool變量,一般以Is作為前綴。另外,如果必要,考慮用屬性來替代方法,具 體建議見10.1.2節;

? 如果方法修改一個成員變量的值,方法名一般為:Set + 成員變量名。同上,考慮 用屬性來替代方法;

3.2.5.變量命名

? 按照使用範圍來分,我們代碼中的變量的基本上有以下幾種類型,類的公有變量;類的私有變量(受保護同公有);方法的參數變量;方法內部使用的局部變量。這些變量的命名規則基本相同,見標識符大小寫對照表。區別如下:

i. 類的公有變量按通常的方式命名,無特殊要求;

ii. 類的私有變量采用兩種方式均可:采用camalString,例如:workerName;采用加“m”前綴,例如mWorkerName;

iii. 方法的參數變量采用camalString,例如workerName;

iv. 方法內部的局部變量采用camalString,例如workerName;

不要用_或&作為第一個字母;

? 盡量使用短而且具有意義的單詞;

? 單字符的變量名一般只用於生命期非常短暫的變量。i,j,k,m,n一般用於integer;c,d,e 一般用於characters;s用於string

? 如果變量是集合,則變量名用復數。例如表格的行數,命名應為:RowsCount;

? 命名組件采用匈牙利命名法,所有前綴均應遵循同一個組件名稱縮寫列表

1.1.1.組件名稱縮寫列表

建議縮寫的基本原則是取組件類名各單詞的第一個字母,如果只有一個單詞,則去掉其中的元音,留下輔音。縮寫全部為小寫。

組件類型

縮寫

例子

Label

Lbl

lblNote

TextBox

Txt

txtName

Button

Btn

btnOK

ImageButton

Ib

ibOK

LinkButton

Lb

lbJump

HyperLink

Hl

hlJump

DropDownList

Ddl

ddlList

CheckBox

Cb

cbChoice

CheckBoxList

Cbl

cblGroup

RadioButton

Rb

rbChoice

RadioButtonList

Rbl

rblGroup

Image

Img

imgBeauty

Panel

Pnl

pnlTree

TreeView

Tv

tvUnit

WebComTable

Wct

wctBasic

ImageDateTimeInput

Dti

dtiStart

ComboBox

Cb

cbList

MyImageButton

Mib

mibOK

WebComm.TreeView

Tv

tvUnit

PageBar

Pb

pbMaster

4. 聲明

? 每行只有一個聲明,如果是聲明i,j,k之類的簡單變量可以放在一行;

? 除了for循環外,聲明放在塊的最開始部分。for循環中的變量聲明可以放在for語句中。如:for(int i = 0; I < 10; i++) 。

避免塊內部的變量與它外部的變量名相同。

5. 表達式和語句

? 每行建議只有一條語句。

? 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;

}

6. 類型設計規範

? 確保每個類型由一組定義明確,相互關聯的成員組成,而不僅僅是一些無關功能的隨 機集合;

? 不要隨意使用public去定義一個類的範圍

6.1.類型和命名空間

? 用命名空間把類型組織成相關域的層次結構。例如:

界面層:GTA.ProductionCenter;

業務邏輯層:GTA.ProductionCenter.Business;

數據訪問層:GTA.ProductionCenter.Data;

避免過深的命名空間;

避免太多的命名空間;

6.2.抽象類設計

不要在抽象類中定義公有的或內部受保護的構造函數。因為抽象類無法實例化,所以這 種設計會誤導用戶;

? 為抽象類定義受保護的構造函數或內部構造函數;

6.3.靜態類設計

靜態類是一個只包含靜態成員的類,它提供了一種純面向對象設計和簡單性之間的一個權衡,廣泛用來提供類似於全局變量或一些通用功能。

? 少用靜態類。靜態類應該僅用作輔助類;

避免把靜態類當作雜物箱。每個靜態類都應該有其明確目的;

? 不要在靜態類中聲明或覆蓋實例成員;

6.4.枚舉設計

? 用枚舉來加強那些表示值的集合的參數,屬性以及返回值的類型性;

? 優先使用枚舉而不是靜態常量。例如:

//不好的寫法

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

}

不要把枚舉用於開放的場合,例如操作系統的版本,朋友的名字等;

枚舉最後一個值不要加逗號;

′ 枚舉中不要提供為了今後使用而保留的枚舉值;

7. 成員設計規範

方法,屬性,事件,構造函數以及字段等統稱為成員

7.1.成員設計的一般規範

避免將不需要public的成員也public,盡量使用private或internal

不要畫蛇添足,增加一些無用的成員

7.2.方法的重載規範

避免在重載中隨意的給參數命名。如果兩個重載中的某個參數表示相同的輸入,那麽該參數的名字應該相同。例如:

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) { ...}

}

避免使重載成員的參數順序不一致。在所有的重載中,同名參數應該出現在相同的位置。 例如:

public class EventLog

{

public EventLog();

public EventLog(string logName);

public EventLog(string logName, string machineName);

public EventLog(string logName, string machineName, string source);

}

? 較短的重載應該僅僅調用較長的來實現。另外,重載如果需要擴展性,把最長重載 做成虛函數。例如:

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。這樣做是為了避免調用者調用之前需要檢查參數是否null。例 如:

//允許為null時的調用

DrawGeometry(brush, pen, geometry);

//不允許為null時的調用

if (geometry == null) DrawGeometry(brush, pen);

else DrawGeometry(brush, pen, geometry);

7.3.屬性和方法的選擇

? 基本原則是方法表示操作,屬性表示數據。如果其他各方面都一樣,優先使用屬性而不 是方法。

? 使用屬性,如果該成員表示類型的邏輯attribue

? 如果屬性的值存儲在內存中,而提供屬性的目的僅僅是為了訪問該值,使用屬性而不 要使用方法

? 如果該操作每次返回的結果不同,那麽使用方法。例如來自於.net framework的例子:

//好的寫法

Guid.NewGuid();

//不好的寫法

DateTime.Now;

? 如果該操作比訪問字段慢一個或多個數量級,使用方法。

? 如果該操作有嚴重的副作用,使用方法。

7.4.屬性的設計規範:

? 如果不應該讓調用方法改變屬性值,創建只讀屬性;

不要提供只寫屬性;

? 為所有的屬性提供合理的默認值,這樣可以確保默認值不會導致漏洞或效率低的代 碼;

? 允許用戶以任何順序來設置屬性的值;

? 避免在屬性的獲取方法拋出異常。

屬性的獲取方法應該是個簡單的操作,不應該有任何的條件。如果一個獲取方法會拋出 異常,按麽可能它更應該設計為方法。

7.5.構造函數的設計規範

? 建議提供簡單的構造函數,最好是默認構造函數。簡單的構造函數增強易用性;

? 考慮擴展性,如果構造函數設計的不自然,建議用靜態的工廠方法來替代構造函數;

? 把構造函數的參數用作設置主要屬性的便捷方法。如果構造函數參數僅用來設置屬 性,應和屬性名稱相同。僅有大小寫的區別;

? 在構造函數中做最少的工作。任何其他處理應該推遲到需要的時候;

? 在類中顯示的聲明公用的默認構造函數,如果這樣的構造函數是必須的。

如果沒有顯示默認構造函數,填加有參數構造函數時往往會破壞已有使用默認構造函數 的代碼;

避免在對象的構造函數內部調用虛成員。這樣在擴展設計的時候會導致難以理解的現 象;

7.6.字段設計規範

不要提供公有的或受保護的字段。代之以屬性來訪問字段;

? 只用常量字段來表示永遠不會改變的量。否則會導致兼容性問題。下面是正確的例子:

public struct Int32

{

public const int MaxValue = 0x7fffffff;

public const int MinValue = unchecked((int)0x80000000);

}

? 用公有的靜態只讀字段來定義預定義的對象實例。例如:

public struct Color

{

public static readonly Color Red = new Color(0x0000FF);

}

7.7.參數的設計規範

? 用類結構層次中最接近基類類型來作為參數的類型,同時要保證該類型能夠提供成員 所需的功能。例如:

要設計一個集合遍歷的方法,那麽參數應該是IEnbumerable為參數,而不應該是IList, 這樣方法具有更強的適應性。

不要使用保留參數。如果將來需要更多的參數,那麽可以增加重載成員。例如:

//不好的寫法

public void Method(string reserved, SomeOption option);

//好的寫法

public void Method(SomeOption option);

//將來填加

public void Method(SomeOption option, string path);

7.7.1.參數設計中枚舉和布爾參數的選擇規範

? 用枚舉。在代碼閱讀,書寫中,枚舉都比布爾的可讀性好很多。例如:

//使用布爾型,閱讀的時候不會輕易了解參數的含義

FileStream f = File.Open(“1.txt”, true, false);

//使用枚舉型

FileStream f = File.Open(“1.txt”,CasingOptions.CaseSenstive, FileMode.Open);

不要使用布爾參數,除非百分之百肯定絕對不需要兩個以上的值。即使此時,采用枚舉 往往也可以提供更好的可讀性,如上例。

? 考慮在構造函數中,對確實只有兩種狀態值的參數以及用來初始化布爾屬性的參數使用 布爾類型;

10.7.2. 參數驗證的規範:

? 驗證傳給公有的,受保護的或顯示成員的參數是否合法。如果驗證失敗,應該拋出 System.ArgutmentException或其子類;

? 拋出System.ArgutmentNullException,如果傳入的null,而該成員不支持null;

10.7.3. 參數傳遞的規範:

避免使用輸出參數或引用參數;

8. 擴展性設計規範

′ 如果沒有恰當理由,不要把類密封起來。這些理由包括:

A)類為靜態類;

B)類的受保護成員保存了高度機密信息;

C)類繼承了許多虛成員,逐個密封的代價太高,不如密封整個類;

D)不要在密封類中聲明保護成員或虛成員,因為無法覆蓋其實現;

? 建議用保護成員用於高級定制。它提供了擴展性,同時也避免了公用接口過於復雜;

不要使用虛成員,除非有合適的理由;

? 建議只有在絕對必須的時候才用虛成員提供擴展性,並使用Template Method模式;

? 優先使用受保護的虛成員,而不是公有虛成員。公有成員通用調用受保護的虛成員的方式來提供擴展性;

9. 異常處理規範

? 異常的思想是只對錯誤采用異常處理:邏輯和編程錯誤,設置錯誤,被破壞的數據,資源耗盡,等等。通常的法則是系統在正常狀態下以及無重載和硬件失效狀態下,不應產生任何異常。異常處理時可以采用適當的日誌機制來報告異常,包括異常發生的時刻;

′ 一般情況下不要使用異常實現來控制程序流程結構;

使用異常而不要用錯誤代碼來報告錯誤;

? 要通過拋出異常的方式來報告操作失敗。如果成員無法成功地完成它應該做的任務,那麽應該拋出異常;

9.1. 異常類型選擇規範

? 優先考慮使用System命名空間中已有的異常,而不是自己創建新的異常類型;

? 要使用最合理,最具針對性的異常。例如,對參數為空,應拋出System.ArgutmentNullException,而不是System.ArgutmentException

9.2. 異常處理規範

′ 不是百分之百確定的情況,不要吞掉異常;

? 建議捕獲特定類型的異常,如果理解該異常在具體環境當中產生的原因;

不要捕獲不應該捕獲的異常,通常應該允許異常沿著調用棧傳遞;

? 進行清理工作時用try-finally,避免使用try-catch;

? 在捕獲並重新拋出異常時使用空的throw語句,這是保持調用棧的最好方法

9.3. 標準異常類的使用:

9.3.1.Exception與SystemException

不要拋出這兩種類型的異常;

避免捕獲這兩種異常,除非是在頂層的異常處理器中;

9.3.2.InvalidOperationException

? 對象處於不正確狀態時拋出;

9.3.3.ArgumentException,ArgumentNullException,ArgumentOutOfRangeException

? 如果傳入的是無效參數,拋出參數異常,盡可能使用位於繼承層次末尾的類型;

? 在拋出異常時設置ParaName屬性;

9.3.4.NullRefernceException,IndexOutOfRangeException,AccessViolationException

不要顯示拋出或捕獲;

9.3.5.StackOverflowException:

不要顯示拋出或捕獲;

9.3.6.OutOfMemoryException:

不要顯示拋出或捕獲;

9.4. 自定義異常類型設計規則:

避免太深的繼承層次;

? 從已有的異常基類繼承;

? 異常類以“Exception”做為後綴;

? 使異常可序列化,使其能跨應用程序域和遠程邊界仍能正常使用;

? 把與安全性有關的信息保存在私有的異常狀態中

9.5. 異常與性能

? 如果在普通場景都會拋出異常,采用先效驗合法性的方式來避免拋出異常引起的性能 問題;

10. 其他規定

? 為避免頻繁改動代碼,代碼中只寫比較簡單的和不會經常發生變化的SQL,如果SQL 經常發生變化或是比較復雜,存到存儲過程或配置文件中,比如統計用到的SQL;

? 在VS2005開發環境中,采用代碼分析工具來做自動化的代碼分析,以保證代碼質量, 具體的使用建議如下:

A)啟用代碼分析,並設置當風格不符合要求時為錯誤而不是警告;

B)如果不是做代碼審核,此開關應關閉。加上了這個選項的時候編譯很慢;

C)詳設的時候打開開關,檢查詳設是否符合編程規範;

D)所有的選項都應當打開。以下內容需要單獨設置:

編碼

名稱

大類

建議

使用等級

CA2209

程序集應聲明最小安全性

用法規則

不建議使用

警告

CA1814

與多維數組相比,首選使用交錯的數組

性能規則

使用,但降低等級

警告

CA1822

將成員標記為 static

性能規則

較繁鎖,且影響代碼質量

禁用

CA2210

程序集應具有有效的強名稱

設計規則

影響Xcopy部署

禁用

CA1302

不要對區域設置特定的字符串進行硬編碼

全球化規則

很繁瑣,並且工具支持的不好。全球化規則全部禁用

禁用

CA2100

檢查 Sql 查詢中是否有安全漏洞

安全性規則

都采用參數化查詢,有可能會參數過長;如果是內部參數,也不會有安全問題

警告

c#代碼規範