規範正文
前言
本文是一套面向C# programmer 和C# developer 進行開發所應遵循的開發規範。
按照此規範來開發C#程序可帶來如下益處:
•
代碼的編寫保持一致性,
•
提升代碼的可讀性和可維護性,
•
在團隊開發一個項目的狀況下,程序員之間可代碼共享
•
易於代碼的回顧,
本規範是第一版,只適用於通常狀況的通用規範,並不能覆蓋全部的狀況。
產品中全部的代碼需遵循統一的標準。
a. 指定統一編碼風格文檔。
b. 重要的在於讓每一個開發人員都遵照。
c. 將不符合規範的代碼看成錯誤處理。
第1章
格式說明
1.1
基本命名格式
1.1.1
Pascal
每一個單詞首字母大寫,如BaseClass。
1.1.2
camel
第一個單詞首字母小寫,其他單詞首字母大寫,如:baseClass。
1.1.3
Hungarian
類型縮寫+Pascal,其中類型縮寫通常使用類型中關鍵輔音字母,如:btnSubmit。
1.1.4
_camel
下劃線 加 camel 格式,如:_wordDocument。
1.1.5
ACL(ALL_CAPITAL_LETTER)
全部字母大寫,單詞間用下劃線鏈接,如:MESSAGE_MAX_SIZE。
1.1.6
其它原則
除字段外,儘可能不使用下劃線「_」,若是須要使用,下劃線應該表示語意上的分級。
第2章
通常規範
2.1
命名規則
一個好的名字應該要表達它的意義。
2.1.1
變量、參數
名詞、名詞短語。
格式:camel。
通常狀況下應該取有意義的名字。可是計數變量當用在瑣碎的計數循環式更適宜叫i, j, k, l, m, n。
2.1.2
常量
名詞、名詞短語。
格式:ACL。
必須使用限定符。(注:限定符爲private,public,internal,protected等。)
2.1.3
字段
名詞、名詞短語。
格式:_camel或m_camel。如:private int _itemCount; private int m_itemCount;
這樣的格式,能夠在方法中把字段和參數、變量等區分開。
GUI控件字段的格式:Hungarian。
GUI控件字段不一樣於通常的字段,這些控件字段中控件類型是重要的信息,因此這裏使用Hungarian格式。
如:
frm Form
btn Buton
cb 下拉列表框
txt 文本輸入框
lbl 標籤
pic 圖像控件
grd DataGrid
sb 滾動條
lst 列表框
cb CheckBox
rb RadioButton
pb 進度條
tv TreeView
lv ListView
除了常量及靜態只讀字段外,其它字段只能使用private、internal限定符。
必須使用限定符。
2.1.4
屬性
名詞、名詞短語。
格式:Pascal。
必須使用限定符。
通常狀況下,應該取有意義的名字。偶爾能夠考慮使用與其類型相同的名字命名一個屬性。如:
public TreeView TreeView
{
get
{
...
}
set
{
...
}
}
除非是十分簡單的類或者純數據類型(貧血數據模型),不然不要使用屬性的縮寫格式(public TreeView TreeView{get;set;})。
這樣能夠在複雜類中只有字段才能表示數據,因此能夠很清楚的看到當前有哪些數據構成該類。
2.1.5
方法(函數)
動詞、動詞短語。
格式:Pascal。
必須使用限定符。
經常使用的介詞有:Of、In、By、DependOn等。如:
User GetByUserName(string username);
2.1.6
事件
考慮用一個動詞命名事件。用如今和過去時態命名有前綴和複製概念的事件名字。
格式:Pascal。
必須使用限定符。
多使用EventHandler<T>進行定義。
如:public event EventHandler<MouseMoveEventArgs> MouseMove;
2.1.7
類型
通常狀況下,類型都使用Pascal格式來進行命名。
必須使用限定符。
2.1.7.1
接口
使用I字母打頭,而後接Pascal形式,即格式爲:IPascal。如ICollection。
2.1.7.2
枚舉
格式:Pascal。
對於位枚舉(Flags)用複數名字。
2.1.7.3
異常類
格式:Pascal + Exception
自定義異常類以Exception結尾,而且在類名中能清楚的描述出該異常的緣由。好比FileNotFoundException,描述出了某個實體(文件、內存區域等)沒法被找到。
2.1.7.4
事件委託
格式:Pascal + EventHandler
如:好比public delegate void MouseEventHandler (object sender, MouseEventArgs e); //用於處理與鼠標相關的事件或委託。
對於自定義的委託,其參數第一個建議仍然使用object sender,sender表明觸發這個時間或委託的源對象。而第二個參數繼承於EventArgs類,而且在派生類中實現本身的業務邏輯。
2.2
代碼格式及邏輯
2.2.1
聲明
2.2.1.1
類成員聲明
全部類成員都應該明確寫上限定符:private,protected,public,internal。
2.2.1.2
每行的聲明數
每行只有一個聲明,由於它能夠方便註釋。
int level; // indentation level
int size; // size of table
當聲明變量時,不要把多個變量或不一樣類型的變量放在同一行,例如:
int a, b; //What is 'a'? What does 'b' stand for?
2.2.1.3
初始化
局部變量一旦被聲明就要初始化。例如:
string name = myObject.Name;
或
int val = time.Hours;
2.2.1.4
方法聲明
若是能夠的話,方法的參數儘可能不要使用out及ref。
2.2.2
語句
大括號的格式:大括號內表示一個語句塊(block),前大括號和後大括號能夠只佔一行。
每行都應該只包含一條語句。
2.2.2.1
If語句
可使用單行if。
其他狀況應該使用帶括號的if語句。
如,使用:
If(argument <= 0) throw new ArgumentException(「argument必須是正整數!」);
或
If(value)
{
//do something
}
而不是
If(value)
This.Calculate();
推薦不要把邏輯上不是一類的判斷組合在一塊兒。
條件儘可能簡單,不宜過長。
若是條件過長,可採用兩種方式:
1.能夠先把條件的值計算出,並存入一個臨時的變量。
2.可對條件進行折行,折行的方式見:2.4.5.2換行。
2.2.2.2
For語句
帶上兩個括號,即採用如下格式:
for (int i = 0; i < array.Length; i++)
{
//do sth
}
當斷定條件比較耗時時,應該採用緩存變量的形式,即如下格式:
for( int i = 0, c = list.Count; i < c; i++)
{
//do sth
}
2.2.2.3
While語句
採用如下格式:
while(value)
{
}
或
do
{
}
while(value)
2.2.2.4
Switch語句
switch應該處理全部的可能分支。若是不處理時,應該拋出異常。如:
switch(value)//value 是string類型
{
case 「1」:
//case 1;
break;
case 「2」:
//case 2;
break;
default:
throw new NotSupportedException();
}
2.2.2.5
Try語句
格式以下:
try
{
}
catch(NotSupportedException ex)//子異常
{
}
catch(Exception e)//基異常
{
}
finally//finally語句能夠省略
{
}
2.2.2.6
小技巧
以上全部語句,在VS環境下,都已經默認攜帶了snippet方便快捷輸入。如在書寫if語句時,可輸入「if」,而後按兩下tab鍵便可。
另外,它的用處在於它能夠進行自定義你常常輸入的代碼段。具體內容見:http://www.cnblogs.com/WilsonWu/archive/2010/02/25/1673745.html
2.2.3
屬性
不要連續地對同一屬性進行調用。
如:
var prop2 = this.Prop1.Prop2;
var prop3 = this.Prop1.Prop3;
而是應該用變量進行緩存,如:
var prop1 = this.Prop1;
var prop2 = prop1.Prop2;
var prop3 = prop1.Prop3;
這是由於屬性其實就是方法。因此就算是最簡單的屬性,對它進行屢次調用都會浪費必定的資源。
屬性的內部代碼,不要設計得太過複雜。方便調試。
最好不要訪問易中斷的外部資源。由於極可能會致使沒法對其調試。
2.2.4
事件
監聽的事件處理方法取名按照「必定規則」,如btnSubmit_Click。監聽的事件處理方法裏面,不該該寫比較複雜的代碼。若是比較複雜,應該提取函數。
2.2.5
方法
原則上單個方法的長度不能太長,如不超過50行、不超過一屏。
程序編碼力求簡潔,結構清晰,避免太多的分支結構及太過於技巧性的程序。
程序代碼要嚴格,要把全部狀況都考慮到。拋出的異常,應該都是在乎料範圍內的。如,如下代碼是不嚴格的:
int[] array = this.GetIntArray();//這個方法可能會返回一個空數組。
int first = array[0];//因爲數組可能沒有元素,因此這裏可能會拋出異常。
好的代碼應該是:
if(array.Length > 0)
{
int first = array[0];
}
或斷言:
if(array.Length == 0) throw new InvalidException(「返回的數組沒有元素。」)
若是是未完成的方法,必須拋出NotImplementException。
參數的檢測,使用正常的代碼進行檢測。而調試期代碼則使用Debug.Assert方法。
方法邏輯:
按照如下順序編寫:
1.參數檢查。(private方法在參數傳入時已經確定參數正確時,能夠忽略此步。)
2.參數轉換。(private方法在參數傳入時已經確定參數正確時,能夠忽略此步。)
3.核心邏輯。
4.返回值轉換。
5.返回。
2.3
註釋
註釋儘可能使用中文。
添加註釋的目的:使代碼易讀、易寫、易維護 。
如何添加註釋:
a. 代碼、數據、算法的解釋
b. 作標記(時間、所作的改動等)
c. 標識代碼的功能和目的
d. 代碼如何調用
避免
a. 對顯而易見的內容進行註釋
b. 添加大段註釋
c. 註釋的拼寫錯誤
2.3.1
文件頭
爲了清晰的說明代碼功能,跟蹤版本紀錄,應該在源代碼文件中加入相應的描述信息,本文將列舉相應的文件頭格式。
須要說明的是,文件頭中的歷史紀錄位置應該詳細記錄每次所做修改的內容。每次修改版本號都應該做相應的變動。
如對版本號無特殊要求,請遵守以下通用原則:
版本號形如 X.Y,其中X爲大版本號,Y爲小版本號。
bug修正,變動小版本號。
新增功能或重大bug修正,變動大版本號。
2.3.1.1
C#代碼文件頭
/*******************************************************
*
* 做者:胡慶訪
* 建立日期:20150801
* 說明:此文件只包含一個類,具體內容見類型註釋。
* 運行環境:.NET 4.0
* 版本號:1.0.0
*
* 歷史記錄:
* 建立文件 胡慶訪 20150801 17:13
*
*******************************************************/
2.3.1.2
小技巧
能夠在 Visual Studio 中搜索 HxyUtils 插件擴展,以下圖:
安裝後,點擊工具->添加文件頭,便可爲當前打開的代碼文件加入可配置的文件頭。如圖:
生成:
2.3.2
類和函數
每一個類及函數必須寫上註釋。
格式:利用VS自帶的註釋塊生成格式。在須要的地方鍵入"///"。
類型註釋、文件頭、複雜函數、複雜屬性、接口所有內容必須寫清楚註釋。
2.3.3
塊註釋
塊註釋一般應該是被避免的。推薦使用//註釋做爲C#的標準聲明。若是但願用塊註釋時你應該用如下風格:
由於樣能夠爲讀者將註釋塊與代碼塊區分開。雖然並不提倡使用C風格的單行註釋,但你仍然可使用。一旦用這種方式,那麼在註釋行後應有斷行,由於很難看清在同一行中前面有註釋的代碼:
/* blah blah blah */
塊註釋在邏輯比較複雜時進行註釋是很是有用的。一般塊註釋用於註釋掉大的代碼段。
2.3.4
單行註釋
你應該用//註釋風格「註釋掉」代碼(快捷鍵,Ctrl+K,Ctrl+C)。它也能夠被用於代碼的註釋部分。
單行註釋被用於代碼說明時必須縮進到相應的編進層級。
(一條經驗,註釋的長度不該該超過被解釋代碼的長度太長,由於這表示代碼過於複雜,有潛在的bug。)
2.4
代碼組織
2.4.1
命名空間
命名空間的格式也是Pascal命名法。
邏輯上可分爲子空間的,儘可能分子空間。
2.4.2
目錄結構
爲每個命名空間建立一個目錄。(用MyProject/TestSuite/TestTier做爲MyProject.TestSuite.TestTier的路徑,而不用帶點的命名空間名作路徑)這樣能夠更容易地將命名空間映射到目錄層次劃分。
若是一個命名空間下的文件過多,也能夠添加子目錄進行管理。這時不須要和空間名保持一致。
2.4.3
C# 源文件
類名或文件名要儘可能簡短,將代碼分割開,使結構清晰。將每一個類儘可能放在一個單獨的文件中,使用類名來命名文件名(固然擴展名是.cs)。這種約定會使你們工做更簡單。
若是是很簡單的一些同類型的類,也能夠把多個類寫在一個文件當中。這時,文件的名字使用最主要的類的類名,或者是爲它們起一個類型名。例如,多個類能夠放一個文件的狀況:枚舉、常量、命令、Attribute。
若是某個類是分部類,分別寫在多個文件中。則須要其中一個文件是主文件,其它文件擴展名分級別。推薦讓它們文件依賴於主文件。(這裏的依賴是指project中的文件依賴關係,如1.aspx.cs依賴於1.aspx文件,這樣vs就會把它們摺疊起來。)如:
(
推薦使用文件依賴:
對應csproject代碼:
<Compile Include="Control.cs" />
<Compile Include="Control.MouseEvent.cs" >
<DependentUpon>Control.cs</DependentUpon>
</Compile>
)
2.4.4
類成員排序
成員按如下次序排序:
常量 static readonly 或 const
靜態私有字段 private static int _count
靜態構造函數 static ClassName(){}
公共靜態屬性 public static int Count{get;set;}
公共靜態方法 public static void AddCount(){}
私有字段 private int _count
構造函數 public ClassName(){}
公共屬性 public int Count{get;set;}
公共方法 public void AddCount(){}
事件 public event EventHandler CountChanged;
保護方法 protect void OnCountChanged(){}
私有方法 private void AddCount();
保護靜態方法 protected static void HelperMethod(){}
私有靜態方法 private static void HelperMethod(){}
內部類 private class InnerClass{}。
不一樣類型的成員,使用區域或空格進行分隔。
同種類型的成員,先按該成員的重要程度進行排序,同時兼顧它們之間的關係。
排序原理:
常量命名從類內部的第1行開始。
緊接着是字段,字段表示數據,可以一目瞭然地表現類的結構。
而後是:構造函數、屬性、事件、方法。
保護的靜態方法和私有靜態方法通常被本類的實體方法看成公共方法調用,因此放在最後。
修飾符按可見度進行排序,即:public internal protected private。
2.4.5
空白與分隔
2.4.5.1
空白
2.4.5.1.1
縮進
縮進不使用製表符,而是使用空白符(這是由於製表符在不一樣的編輯器下,顯示的效果不同,因此都使用空白)。每次縮進的空白字符數是4。
2.4.5.1.2
內部空格
在一個逗號或一個分號以後應該由一個空格,例如:
TestMethod(a, b, c);
不要用:
TestMethod(a,b,c)
或
TestMethod( a, b, c );
單個空格包圍操做符(除了像加的一元操做符和邏輯非),例:
a = b; // don't use a=b;
for (int i = 0; i < 10; ++i) // don't use for (int i=0; i<10; ++i)
// or
// for(int i=0;i<10;++i)
2.4.5.1.3
小技巧
可使用VS IDE自帶的格式化功能,(快捷鍵:Ctrl+K、Ctrl+D)便可自動自成縮進、空格。
2.4.5.2
換行
邏輯換行:
空行提升可讀性。它們分開那些邏輯上自身相關聯的代碼塊。因此,每個空行,都應該表示邏輯上的分隔關係,
類的成員之間,都必須空行。
過長換行:
當一個表達式超過一行時,根據這些通用原則進行處理:
1.
在逗號後換行。
2.
在操做符後換行。
3.
在高層換行而不要在低層處換行。
4.
一次或屢次折行後,都對應此行語句最上層的表達式起始位置,進行一次縮進。
方法調用換行示例:
longMethodCall(expr1, expr2,
expr3, expr4, expr5);
算術表達式換行示例:
推薦:
var x = a * b / (c - g + f) +
4 * z;
很差的格式——應避免:
var x = a * b / (c - g +
f) + 4 * z;
推薦使用第一種方法,由於是在括號表達式以外折行(高層次折行原則)。注意要用製表符到縮進的位置,而後用用空格到折行的位置。在咱們的例子中是:
> var x = a * b / (c - g + f) +
> ......4 * z;
'>'表示是製表符,'.'表示是空格符。(製表符後是空白是用製表符縮進)。一個好的編碼習慣就是在所用的編輯器中顯示製表符和空格符。
2.4.5.3
Region
邏輯上的獨立塊,都推薦使用Region進行分隔。
方法內不要使用Region,若是太長,就提取方法。
空行可進行邏輯塊的分隔。可是當邏輯塊較大時,應該使用Region。
格式:#region 和 #endregion的先後都須要有一行空行。若是添加的空行是在block的開始和結尾,則能夠省略。
如:
public class Class1
{
#region 私有字段
字段定義
#end region
#region 公共屬性
屬性的定義
#endregion
}
2.4.5.4
空代碼塊
大括號內的代碼塊爲空時,應把兩個大括號都和前一行代碼放在一行。如:
protected virtual void OnMove(EventArgs e) { }
try
{
//…
}
catch{ }//放在一行。
第3章
推薦基類編寫規範
此規範較爲嚴格,僅爲推薦。適用於編寫較好的框架基類,特殊狀況下可不遵照部分規範。
3.1
代碼格式及邏輯
3.1.1
事件
監聽某個事件後,要記住釋放監聽。
3.1.1.1
通常編寫模式(推薦使用)
爲每一個事件都寫一個保護的虛方法。虛方法名爲On + 事件名,並只帶一個對應事件的事件參數。
在虛方法中觸發外部監聽的事件。
如:
public event EventHandler<MouseMoveEventArgs> MouseMove;
protected virtual void OnMouseMove(MouseMoveEventArgs e)
{
var handler = this.MouseMove;
if(handler != null) handler(this, e);
}
子類在處理事件時,直接重寫該虛方法。
如:
protected override void OnMouseMove(MouseMoveEventArgs e)
{
base.OnMouseMove(e);
//do sth.
}
3.1.2
異常
3.1.2.1
描述
異常信息應該有以下的描述
存在的問題。
違反的規律。
可能的解決方案。
如:Nod沒法打開文件nod.config:請確認該文件存在而且當前用戶擁有足夠的權限
如:OPath解析查詢語句錯誤:[和]必須匹配,請檢查[]的完整性
3.1.2.2
處理
發生異常時只能向外拋出。
第4章
DBI應用編寫規範
4.1
代碼組織
4.1.1
命名空間
命名空間使用如下格式:
實體類:DBI.(可選子模塊縮寫).(可選命名空間);
服務:DBI.Services.(可選子模塊縮寫);
命令:DBI.WPF. (可選子模塊縮寫).Commands;
界面:DBI.WPF. (可選子模塊縮寫)。
例如,在用戶模塊中的實體類都聲明在 DBI.Accounts 空間下,命令則聲明在 DBI.WPF.Accounts.Commands 空間下,界面其它類型則聲明 DBI.WPF.Accounts 中。
4.1.2
類型
命令類型:因爲全部命令都已經放在 Commands 命名空間下,因此這些類型都不須要以 Command 結尾。
第5章
代碼管理
5.1
版本控制
採用版本控制工具svn進行源代碼管理。(將來將採用 Git 進行管理)
天天打開解決方案後,先獲取最新版本。
每次提交都應該保證沒有編譯錯誤,同時在說明欄寫上本次完成的內容。
提交流程:更新最新版本;編譯無錯;提交。
5.2
版本號
給客戶發佈的全部版本須要使用版本號進行管理。
版本號由四個部分組成:主版本號、次版本號、內部版本號和修訂號。形式如:
主版本號.次版本號.編譯版本號.修正版本號
全部定義的部分都必須是大於或等於 0 的整數。
主版本號:具備相同名稱但不一樣主版本號的程序集不可互換。例如,這適用於對產品的大量重寫,這些重寫使得沒法實現向後兼容性。
次版本號:若是兩個程序集的名稱和主版本號相同,而次版本號不一樣,這指示顯著加強,但照顧到了向後兼容性。例如,這適用於產品的修正版或徹底向後兼容的新版本。
內部版本號:內部版本號的不一樣表示對相同源所做的從新編譯。這適合於更改處理器、平臺或編譯器的狀況。
修正版本號:名稱、主版本號和次版本號都相同但修訂號不一樣的程序集應是徹底可互換的。這適用於修復之前發佈的程序集中的安全漏洞。
程序集的只有內部版本號或修訂號不一樣的後續版本被認爲是先前版本的修補程序 (Hotfix) 更新。
5.3
Build策略
每日至少Build一次。
5.4
Code Review
暫無。