这个文档可以作为一个向导,以写强壮可靠的程序。虽然这里关注的是用C#写程序,但如果使用另外一种编程语言,很多规则和原理也是有用的。
把每个类都放在单独的文件中,文件名字和类名一致(用.CS作为扩展名)。类文件不要太长,不要超过2000LOC。必要时,分割代码,使结构更清晰。
为每个命名空间创建一个目录(如,对于MyProject.TestSuite.TestTier使用MyProject/TestSuite/TestTier作为路径,不要使用带“.”的命名空间)。这样更易于映射命名空间到目录。
如果表达式不适合单行显示,应根据下面通常的原则分行:
n 在一个逗号后换行
n 在一个操作符后换行
n 在表达式的高层次处换行
n 新行与前一行在同一层次,并与表达式的起始对齐
方法分行的例子:
long MethodCall(expr1, expr2,
expr3, expr4, expr5);
算术表达式分行的例子:
好的:
var = a * b / (c – g + f) +
4 * z;
坏的风格,要避免:
var = a * b / (c – g +
f) + 4 * z;
第一个是好的,因为分行符合高层次规则。
不要使用空格缩进 - 使用tabs!
通常要避免块注释,而使用C#标准的///注释来描述。如果希望使用块注释,应该使用下面的风格:
/ * Line 1
* Line 2
* Line 3
*/
块注释很少使用,通常是用来注释掉大块的代码。
应该使用//注释掉一行代码,也可以用它注释掉代码块。当单行注释用来做代码解释时,必须要缩进到与代码对齐。
单行XML注释的形式如下:
/// <summary>
/// This class…
/// </summary>
多行XML注释的形式如下:
/// <exception cref=”BogusException”>
/// This exception gets thrown as soon as a
/// Bogus flag gets set.
/// </exception>
建议每行只有一个声明,还方便注释,如:
int level; // indentation level
int size; // size of table
变量的命名意义要明确。如果能够自解释,如indentLevel,就不用注释。
不好的:
int a, b; // What is ‘a’? What does ‘b’ stand for?
尽量在局部变量声明时进行初始化,例如:
string name = myObject.Name;
或
int val = time.Hours;
注意:初始化对话框时,尽量使用语句:
Using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
……
}
当写C#类和接口时,应按照下面的格式规则:
n 在方法名字和参数列表的起始括号“(”之间没有空格
n 开括号“{”应出现在声明语句之后的下一行
n 闭括号“}”自己占一行,并缩进到对应的开括号位置
例如:
class MySample : MyClass, IMyInterface
{
int myInt;
public MySample(int myInt)
{
this.myInt = myInt;
}
void Inc()
{
++myInt;
}
void EmptyMethod()
{
}
}
每行应该只包含一个语句。
返回语句不应该带有最外面的括号。
不应该使用:
return (n * (n + 1) / 2);
应该使用:
return n * (n + 1) / 2;
if, if – else和if else – if else语句应该按照下面格式:
if (condition)
{
……
}
if (condition)
{
……
}
else
{
……
}
if (condition)
{
……
}
else if (condition)
{
……
}
else
{
……
}
注意:即使某条件下只有一个语句,也要使用大括号“{”“}”。后面的循环等语句也一样。
for语句形式如下:
for (int i = 0; i < 5; ++i)
{
……
}
或者使用单行形式:
for (initialization; condition; updat);
单行形式可考虑使用while语句代替。
foreach语句如下:
foreach (int i in intList)
{
……
}
while语句如下:
while (condition)
{
……
}
空的while语句形式如下:
while (condition);
do – while语句如下:
do
{
……
}
while (condition);
switch语句形式如下:
switch (condition)
{
case A :
……
break;
case B :
……
break;
default :
……
break;
}
try – catch语句形式如下:
try
{
……
}
catch (Exception e)
{
……
}
或者
try
{
……
}
catch (Exception e)
{
……
}
finally
{
……
}
属性形式如下:
public string Name
{
get
{
……
}
set
{
……
}
}
对于抽象属性:
public string Name
{
get;
set;
}
枚举形式如下:
public enum Color
{
Red,
Green,
Blue
}
使用空行按照逻辑关系分隔代码,能提高可读性。
在下面元素之间要使用一个空行:
n 构造函数
n 属性
n 方法
n 方法内的逻辑块
在逗号或分号之后应该有一个空格,例如:
应该使用:
TestMethod(a, b, c);
不应该使用:
TestMethod(a,b,c);或者TestMethod( a, b, c );
操作符两边要有一个空格(递增和逻辑否等一元操作符除外),例如:
应该使用:
a = b;
不应该使用:
a=b;
应该使用:
for (int i = 0; i < 10; ++i)
不应该使用:
for (int i=0; i<10; ++i)或者for(int i=0;i<10;++i)
行逻辑块应该写成类似表格格式:
string name = “Mr. Ed”;
int myValue = 5;
Test aTest = Test.TestYou;
大写每一个单词的第一个字符,如TestCounter。
除了第一个单词,大写其它单词的第一个字符,如testCounter。
如果一个标志符是只包含1,2或3个字符的缩写,可以全部大写,例如:
public class Math
{
public const double PI = ......
}
在.Net中,通常认为使用下划线和匈牙利符号命名是不好的。
匈牙利符号定义了一组名字的前缀和后缀,来表示变量的类型。这种命名风格在早期的windows编程中广泛使用。在MFC编程中,还习惯使用m_作为成员变量的前缀。这些风格在.Net中不应该使用。记住:一个好的变量名字应该描述语义,而不是类型。
但GUI代码是个例外。所有GUI元素类型(如Button)的域和变量名字,应该以它的类型全名作为后缀,例如:
System.Windows.Forms.Button cancelButton;
System.Windows.Forms.TextBox nameTextBox;
n 名字必须是名词或名词短语
n 异常类要以Exception作为后缀
n 不要使用任何前缀
n 使用Pascal风格
n 用名词,名词短语或者描述行为的形容词命名接口。(如IComponent,IEnumbetable)
n 使用Pascal风格
n 使用I作为名字的前缀,I后面的字符(接口名字的第一个字符)要大写
n 枚举类型名和值的名字都使用Pascal风格
n 枚举类型和值都没有前缀和后缀
n 使用描述性的名字,能充分表示出变量的含义
n 对于静态的readonly和const域,用名词,名词短语或名词缩写命名
n public域使用Pascal风格
n protected和private域使用Camel风格
n 使用描述性的名字,能充分表示出变量的含义
n 使用Camel风格
n 尽量使用含义明确的名字
n 如果变量仅用来在循环中计数,应优先使用i,j,k,l,m,n
n 使用Camel风格
n 用动词或动词短语命名
n 使用Pascal风格
n 使用名词或名词短语命名
n 使用Pascal风格
n 事件控制器要带有EventHandler后缀
n 使用sender和e命名两个参数
n 事件参数类要带有EventArgs后缀
n 考虑使用动词命名事件
n 对于有“之前”或“之后”概念的事件,要使用现在时或过去时命名
n 使用Pascal风格
书写类时,按照从上到下的顺序,类成员应该是域,构造函数,属性,方法。
类的域都应该是private,如果需要被外部访问,使用public属性进行访问。但也有例外:
n 如果类的域仅作为一种数据集合,可以将域设定为public,这样的类不要有任何方法
n 常量域,静态域或静态常量域可以设定为public
private是默认的类成员访问修饰符,可以省略,但为了明确表达,应该书写出来。
类的功能要单一,不要组合没有直接关联的功能。方法也一样。一个方法只完成一个任务,不要把多个任务组合进一个方法,即使那些任务很小。就是说,应该以逻辑功能来界分类或方法。
不要使用数字或字符串来指示离散值,应该使用枚举。
不好的:
void SendMail(string message, string mailType)
{
switch (mailType)
{
case "Html" :
……
break;
case "PlainText" :
……
break;
case "Attachment" :
……
break;
default :
……
break;
}
}
好的:
enum MailType
{
Html,
PlainText,
Attachment
}
void SendMail(string message, MailType mailType)
{
switch (mailType)
{
case MailType.Html :
……
break;
case MailType.PlainText :
……
break;
case MailType.Attachment :
……
break;
default :
……
break;
}
}
只捕获特定的异常,如读取外部文件,而不要捕获一般异常,有利于调试排错。
不要捕获了异常却什么也不错,应该做适当处理,并把结果反馈到界面。如果隐藏了一个异常,将很难知道异常到底发生了没有,或者在哪里发生的。