1、引言
本文是一套面向C# programmer和C# developer進行開發所應遵循的開發規范
按照此規范來開發C#程序可帶來以下益處:
代碼的編寫保持一致性,提高代碼的可讀性和可維護性,在團隊開發一個項目的情況下,程序員之間可代碼共享,易於代碼的回顧
本規范是初版,只適用於一般情況的通用規范,並不能覆蓋所有的情況
2、文件組織
2.1C#源文件
類名或文件名要簡短,不要超過2000LOC,將代碼分割開,使結構清晰。將每個類放在一個單獨的文件中,使用類名來命名文件名(當然擴展名是.cs),這種約定會使大家工作更簡單
2.2目錄設計
為每一個命名空間創建一個目錄。(用MyProject/TestSuite/TestTier作為MyProject.TestSuite.TestTier的路徑,而不用帶點的命名空間名做路徑)這樣可以更容易地將命名空間映射到目錄
層次划分。
3、縮進
3.1換行
當一個表達式超過一行時,根據這些通用原則進行處理:
在逗號后換行,在操作符后換行,在高層換行而不要在低層處換行,折行后對齊上一行語句同一層的表達式起始位置。
方法調用換行示例:
longMethodCall(expr1,expr2,expr3,expr4,expr5);
算術表達式換行示例:
推薦:
var = a * b / (c - g + f) +
4 * z;
不好的格式——應避免:
var = a * b / (c - g +
f) + 4 * z;
推薦使用第一種方法,因為是在括號表達式之外折行(高層次折行原則)。注意要用制表符到縮進的位置,然后用用空格到折行的位置。在我們的例子中是:
> var = a * b / (c - g + f) +
> ......4 * z;
'>'表示是制表符,'.'表示是空格符。(制表符后是空白是用制表符縮進)。一個好的編碼習慣就是在所用的編輯器中顯示制表符和空格符。
3.2 空白
利用空格進行縮進從未有過統一的標准。一些人喜歡用兩個空格,一些人喜歡用四個空格而還有一些人喜歡用八個空格,甚至有的人喜歡用更多的空格。好的做法是用制表符。制表符有一些優點:
· 每個人都可以設置他們自己喜歡的縮進層級。
· 它僅僅是1個字符而不是2,4,8等等,因此它將減少輸入(甚至因為自動縮進,有時你不得不手工設置縮進或取消設置,等等諸如此類的操作)。
· 如果你想增加或減少縮進,可以標記一塊,使用Tab增加縮進層級而用Shift-Tab減少縮進層級。這幾乎對於任何文本編輯器都是適用的。
這里,我們定義制表符為標准縮進符。
不要用空格縮進—用制表符!
4、注釋
4.1塊注釋
塊注釋通常應該是被避免的 。推薦使用///注釋作為C#的標准聲明。如果希望用塊注釋時你應該用以下風格:
/*Line1
*Line2
*Line3
*/
因為這樣可以為讀者將注釋塊與代碼塊區分開。雖然並不提倡使用C風格的單行注釋,但你仍然可以使用。一旦用這種方式,那么在注釋行后應有斷行,因為很難看清在同一行中前面有注釋的代碼
/*blah blah blah */
塊注釋在極少情況下是有用的。通常塊注釋用於注釋掉大的代碼段
4.2單行注釋
你應該用//注釋風格"注釋掉”代碼(快捷鍵,Alt+/),它也可以被用於代碼的注釋部分
單行注釋被用於代碼說明時必須縮進到相應的編進層級。注釋掉的代碼應該放在第一行被注釋掉以使注釋掉的代碼更容易看清
一條經驗,注釋的長度不應該超過被解釋代碼的長度太長,因為這表示代碼過於復雜,有潛在的bug
4.3文件注釋
在.net框架,Microsoft已經介紹了一個基於XML注釋的文件。這些文件是包括XML標簽的正規的單行的C#注釋。他們遵循單行注釋的模式:
///<summary>
///This class
///</summary>
多行XML注釋遵循這種模式:
///<exception cref="BogusException">
///This exception gets thrown as soon as a
///</exception>
為了被認作是XML注釋行,所有的行都必須用三個反斜線開始。標簽有以下兩類:
文件說明項 格式/參考
第一類包括像<summary><param>or<exception> 的標簽。描述一個程序的API元素的這些文檔說明項必須寫清楚以方便其他程序員。如上面的多行注釋示例所示,這些標簽通常帶有名
稱或cref屬性。編譯器會檢查這些屬性,所以它們必須是有效正確的。第二類用諸如<code>,<list>or<para>標簽,用於控制備注說明的布局。文件可以用‘文件’菜單中的“創建”菜單產生。文件
以html格式產生:
5、聲明
5.1每行的聲明數
推薦每行只有一個聲明,因為它可以方便注釋
int level;//indentation level
int size;//size of table
當聲明變量時,不要把多個變量或不同類型的變量放在同一行,例如:
int a, b; //What is 'a'? What does 'b' stand for?
上面的例子也顯示了變量名不明顯的缺陷。當命名變量時要清晰。
5.2 初始化
局部變量一旦被聲明就要初始化。例如:
string name = myObject.Name;
或
int val = time.Hours;
注意:如果你初始化一個dialog,設計使用using語句:
using (OpenFileDialog openFileDialog = new OpenFileDialog()) {
...
}
5.3 類和接口聲明
當編寫C#類和接口時,應遵循以下格式化規則:
· 在方法名和圓括號“(”開始它的參數列表之間不要使用空格。
· 在聲明語句的下一行以大括號"{"標志開始。
· 以"}"結束,通過它自身的縮進與相應的開始標志匹配。
例如:
Class MySample : MyClass, IMyInterface
{
int myInt;
public MySample(int myInt)
{
this.myInt = myInt ;
}
void Inc()
{
++myInt;
}
void EmptyMethod()
{
}
}
對於一個大括號的位置參考10.1部分。
6. 語句
6.1 簡單語句
每行都應該只包含一條語句。
6.2 返回語句
一個返回語句不要用最外圍圓括號。不用:
return (n * (n + 1) / 2);
用: return n * (n + 1) / 2;
6.3 If, if-else, if else-if else 語句
if, if-else and if else-if else 語句看起來應該像這樣:
if (condition) {
DoSomething();
...
}
if (condition) {
DoSomething();
...
} else {
DoSomethingOther();
...
}
if (condition) {
DoSomething();
...
} else if (condition) {
DoSomethingOther();
...
} else {
DoSomethingOtherAgain();
...
}
6.4 for / foreach 語句
一個for語句應該如下形式:
for (int i = 0; i < 5; ++i) {
...
}
或者放置一行(考慮用一個while語句代替)
for (initialization; condition; update) ;
foreach語句應該像下面所示 :
foreach (int i in IntList) {
...
}
注意:在一個循環中,即使只有一個語句通常也用括弧括起來。
6.5 While/do-while 語句
一個while語句應該寫成如下形式:
while (condition) {
...
}
一個空while語句應該是以下格式:
while (condition) ;
一個do-while語句應該是如下格式:
do
{
...
} while (condition);
6.6 Switch 語句
一個switch語句應該如下格式:
switch (condition) {
case A:
...
break;
case B:
...
break;
default:
...
break;
}
6.7 Try-catch 語句
一個try-catch statement語句應該遵循以下格式:
try {
...
} catch (Exception) {}
or
try {
...
} catch (Exception e) {
...
}
or
try {
...
} catch (Exception e) {
...
} finally {
...
}
7. 空白
7.1 空行
空行提高可讀性。它們分開那些邏輯上自身相關聯的代碼塊。兩行空格行應該用於以下之間:
· 一個源文件的邏輯段。
· 類和接口定義(每個文件只定義一個類或接口以避免這種情況)。
一個空格行應該總是被用於以下之間:
· 方法
· 屬性
· 一個方法中的局部變量和它的第一條語句
· 一個方法中的邏輯段為了提高可讀性。注意空白行必須被縮進因為它們包括一條語句這使得插入這些行更容易。
7.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)
7.3 表格格式化
行的一個邏輯塊應該作為一個表格被格式化:
string name = "Mr. Ed";
int myValue = 5;
Test aTest = Test.TestYou;
對於表格的格式化用空格而不用制表符因為在某些制表符縮進設置會使表格格式化看起來是很奇怪。
8. 命名習慣
8.1 大寫格式
8.1.1 Pascal Casing
習慣大寫每個單詞的第一個字母(就像在TestCounter)。
8.1.2 Camel Casing
習慣除了第一個單詞外大寫每個單詞的第一個字母例如testCounter。
8.1.3 全大寫情況
對於只有一兩個字符縮寫組成的標識符才用全大寫的情況。有三個或更多個字符組成的標識符應該用Pascal情況代替。例如:
public class Math
{
public const PI = ...
public const E = ...
public const feigenBaumNumber = ...
}
8.2. 命名指導方針
通常根據指導方針在名字和命名內用低線字符對Hungarian 符號來說被認為是壞習慣。
Hungarian 符號是一組應用於命名來映射變量類型的前綴和后綴。這種命名風格在早期的Windows程序中被廣泛應用,但現在被取消了至少不提倡了。如果你遵循這個指南用Hungarian 符號是不允許的。
但要記住一個好的變量名描述了語義而不失類型。
對於這個規則有個例外就是GUI編碼。包括像按鈕(buttton)的GUI元素,所有領域和變量名都應該帶有它們類型名的后綴不是縮寫。例如:
System.Windows.Forms.Button cancelButton;
System.Windows.Forms.TextBox nameTextBox;
8.2.1 類命名指導方針
· 類命名必須是名詞或名詞短語。
· UsePascal 情況參考8.1.1
· 不要用任何類前綴
8.2.2 接口命名指導方針Guidelines
· 用可以描述行為的名詞或名詞短語或形容詞命名接口。(例如IComponent 或 IEnumberable)
· 用Pascal情況(參考8.1.1)
· 用I作為名字的前綴,它應該緊跟一個大寫字母(接口名的第一個字母)
8.2.3 枚舉命名指導方針
· 用Pascal情況命名枚舉值名字和類型名字
· 枚舉類型和枚舉值不要前綴
· 對於枚舉用單一名字
· 對於位領域用復數名字
8.2.4 只讀和常量命名
· 用名詞,名詞短語或名詞的縮寫命名靜態領域
· 使用Pascal 情況(參考8.1.1)
8.2.5 參數/非常量領域命名
· 一定要用描述性名字,應該能夠足夠表現變量的意義和它的類型。但一個好的名字應該基於參數的意義。
· 使用Camel情況(參考8.1.2)
8.2.6 變量命名
· 計數變量當用在瑣碎的計數循環式更適宜叫i, j, k, l, m, n。(參考10.2例如對全局計數的更智能命名等等)—
· 使用Camel情況(參考8.1.2)
8.2.7 方法命名
· 用動詞或動詞短語命名方法。
· 使用Pascal(參考8.1.2)
8.2.8 屬性命名
· 用名詞或名詞短語命名屬性
· 使用Pascal 情況(參考8.1.2)
· 考慮用與其類型相同的名字命名一個屬性
8.2.9 事件命名
· 用事件處理器后綴命名事件處理器
· 用sender 和 e命名兩個參數
· 使用Pascal情況(參考8.1.1)
· 用EventArgs 后綴命名事件參數
· 用現在和過去時態命名有前綴和復制概念的事件名字。
· 考慮用一個動詞命名事件。
8.2.10 大寫總結
Type |
Case |
Notes |
Class / Struct |
Pascal Casing |
|
Interface |
Pascal Casing |
Starts with I |
Enum values |
Pascal Casing |
|
Enum type |
Pascal Casing |
|
Events |
Pascal Casing |
|
Exception class |
Pascal Casing |
End with Exception |
public Fields |
Pascal Casing |
|
Methods |
Pascal Casing |
|
Namespace |
Pascal Casing |
|
Property |
Pascal Casing |
|
Protected/private Fields |
Camel Casing |
|
Parameters |
Camel Casing |
|
9. 編程習慣
9.1 可見性
不要任何公共實例或類變量,讓它們為私有的。對於私有成員最好不用“private”作修飾語什么都不寫。私有是默認情況,每個C#程序員都應該知道這一點。
用屬性代替。你可以用公共靜態(或常量)對於這個規則是以例外,帶它不應該是規則。
9.2 沒有“幻”數
不要用幻數,也就是在源代碼中直接用常數值。替代這些后者以防變化(比方說,你的應用程序可以處理3540用戶代替427你的代碼在50行中通過分散25000LOC)是錯誤和沒有收益的。聲明一個帶有數的常量來代替:
public class MyMath
{
public const double PI = 3.14159...
}
10. 編碼舉例
10.1 Brace placement example
namespace ShowMeTheBracket
{
public enum Test {
TestMe,
TestYou
}
public class TestMeClass
{
Test test;
public Test Test {
get {
return test;
}
set {
test = value;
}
}
void DoSomething()
{
if (test == Test.TestMe) {
//...stuff gets done
} else {
//...other stuff gets done
}
}
}
}
括弧應該在以下情況之后以新行開始:
· 命名空間聲明(注意這在0.3版本中是新添的與0.2版本不同)
· 類/接口/結構聲明
· 方法聲明
10.2 變量命名舉例
代替:
for (int i = 1; i < num; ++i) {
meetsCriteria[i] = true;
}
for (int i = 2; i < num / 2; ++i) {
int j = i + i;
while (j <= num) {
meetsCriteria[j] = false;
j += i;
}
}
for (int i = 0; i < num; ++i) {
if (meetsCriteria[i]) {
Console.WriteLine(i + " meets criteria");
}
}
try intelligent naming :
for (int primeCandidate = 1; primeCandidate < num; ++primeCandidate)
{
isPrime[primeCandidate] = true;
}
for (int factor = 2; factor < num / 2; ++factor) {
int factorableNumber = factor + factor;
while (factorableNumber <= num) {
isPrime[factorableNumber] = false;
factorableNumber += factor;
}
}
for (int primeCandidate = 0; primeCandidate < num; ++primeCandidate)
{
if (isPrime[primeCandidate]) {
Console.WriteLine(primeCandidate + " is prime.");
}
}
注意:索引變量通常叫i, j, k 等等。但Note: Indexer variables generally should be called i, j, k etc. But 萬一像這樣,使得重新考慮這個原則更有意義。一般來說,當同一個計數器或索引器被重用,給它們有意義的名字。