C#程序书写规范
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C#程序书写规范
C#程序书写规范
关于C# 型代码指南
这个⽂档是编写稳定可靠的程序的指南。
该⽂章主要着眼于⽤C# 编写的程序,但其中许多的规则和原理甚⾄在使⽤其他编程语⾔时也⼗分有⽤。
⽂件系统(⽂件结构)
源⽂件
尽可能让类名或者⽂件名短⼩,不要超过2000个字符。
把每⼀个类作为⼀个单独的⽂件,并且⽤类的名字来命名该⽂件(当然要加后缀名.cs)。
这个约定可以使得命名更加容易。
路径布局
给每⼀个namespace创建⼀个路径。
(列如⽤MyProject/TestSuite/TestTier作为⽂件MyProject.TestSuite.TestTier的路径,不要使
⽤namespace名字中的点)。
这样可以更容易规划namespace的路径。
缩进
当⼀个表述⽆法⽤⼀⾏写完时,依照下列常⽤规则把它分段:
u 在逗号后断开
u 在运算符前断开
u 使⽤⾼级断点⽽⾮低级断点
分段⽅法的⽰例:
longMethodCall(expr1, expr2, expr3,
expr4, expr5);
算术表达式分段的⽰例:
推荐:
var = a * b / (c - g + f)
+ 4 * z;
避免如下坏形式:
var = a * b / (c - g
+ f) + 4 * z;
第⼀个是标准的,由于断点出现在加上括弧的表达式外边(⾼级准则)。
空格
标准的使⽤空格作为⾏⾸缩进没有固定的格数。
有些⼈喜欢2个空格,有些喜欢4个空格,甚⾄有些是8个或者更多的空格。
现在规定,使⽤默认的4个空格作为缩进。
在缩进的时候,更多的希望使⽤空格⽽不是TAB作为缩进。
注释
块注释
通常我们避免使⽤块注释。
推荐⽤///的形式来给出标准的C#注释。
若要使⽤块注释,那么其形式如下:
/* 第⼀⾏
*第⼆⾏
*第三⾏
*/
这样对于读者来说,块就从代码变成了可读的⽂档。
相类似的,对于单⾏的注释你可以使⽤这种⽼式的C语⾔的注释⽅式,尽管这个是不推荐使⽤的。
但是在这种注释⽅式下,就必须根据注释内容来分⾏,否则就⽆法通过同⼀⾏的注释了解代码的运⾏了:
/* blah blah blah */
块注释只在少数⼏种情况下才有⽤,例⼦参见TechNote“The fine Art of Commenting”。
⼀般来讲,对于段落代码使⽤块注释很有效。
单⾏注释
使⽤//形式来注释单⾏代码(对于单⾏注释有快捷键,CTRL+KC)。
它也可以⽤来给段落代码加注释。
对代码⽂件使⽤单⾏的注释时,必须根据缩进级别来缩进。
被注释的代码必须在第⼀⾏中加以注释,由此增加被注释代码的可读性。
⼀般注释的长度不可以⽐编码解释长很多。
⽂件注释
在.net的框架下,微软引进了⼀种在XML 注释基础上的⽂件⽣成系统。
这种注释形式上就是包括XML标识的C#单⾏注释。
单⾏注释有下⾯这种形式:
/// <summary>
/// This class...
/// </summary>
多⾏XML注释有这样的形式:
/// <exception cref=”BogusException”>
/// This exception gets thrown as soon as a
/// Bogus flag gets set.
/// </exception>
所有的⾏必须加上///才能被认为是XML注释⾏。
XML注释标识分为2类:
l ⽂档元素
l 格式化/参照
第⼀类包括类似于<summary>, <param>或者<exception>的元素。
这些元素是必须写⽂档的,这样相关的程序才能产⽣可以给程序员阅读的⽂档。
后⼀类主管⽂件设计,使⽤类似于<code>,<list>或者<para>的标识,⽣成的⽂件是HTML HELP格式的。
若须关于XML注释详解参看微软.net框架SDK⽂件。
定义变量
每⾏定义变量的个数
推荐每⾏定义⼀个变量,这样便于注释。
换句话说,
int level; // indentation levelint size; // size of table
定义变量时,不要在⼀⾏中放⼊多个变量或者是不同类型的变量。
例如:
int a, b; //'a'是什么? 'b'代表什么?
这个例⼦也显⽰了变量名字不显然的弊端。
命名变量时⼀定要清楚这点。
初始化
局部变量在定义的时候就要初始化。
例如:
string name = ;
或者
int val = time.Hours;
注:如果要初始化⼀个对话框,同时需要释放资源。
要使⽤using 语句:
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
//…
}
类,接⼝和namespace的定义
当编译C# 的类,接⼝和namespace的时候,必须按照⼀下⼏个初始化原则:
l 左⼤括号“{”出现与定义语句的下⼀⾏
l 右⼤括号“}”单独成⼀⾏,与相应的左⼤括号配套。
例如:
class MySample : MyClass, ImyInterface
{
int myInt;
}
namespace Mynamespace
{
// Namespace contents
}
关于⼤括号放置的⽰例参看10.1节。
定义method
关于定义method,要应⽤括号放置原则,⽽且在method的名字跟括号“(”之间不可以有空格,“(”后边跟参数表。
例如:
public MySample(int myInt)
{
this.myInt = myInt;
}
void Inc()
{
++myInt;
}
定义属性,索引器和事件
对于属性,indexer和事件的定义,在定义的同⾏不可以⽤任何括号。
例如:public int Amount
{
get
{
...
}
set
{
...
}
}
public this[string index]
{
get;
set;
}
public EventHandler MyEvent
{
add
{
...
}
remove
{
...
}
}
语句
简单语句
每⼀⾏仅有⼀个语句。
语句
Return语句使⽤时,外⾯不可以加圆括号。
不⽤:return (n*(n+1)/2) ;
⽤:return n*(n+1)/2 ;
语句
If , if-else, if else-if else 语句的形式应该是这样的:if (condition)
{
DoSomething();
...
}
if (condition)
{
DoSomething();
...
}
else
{
DoSomethingOther();
...
}
if (condition)
{
DoSomething();
...
}
else if (condition)
{
DoSomethingOther();
...
}
else
{
DoSomethingOtherAgain();
...
}
语句
For语句有下⾯这种形式:
for (int i = 0; i < 5; ++i)
{
...
}
或者单⾏的(考虑使⽤while语句来代替):
for (initialization; condition; update)
;
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) {
}
或者
try
{
...
}
catch (Exception e) {
...
}
或者
try
{
...
}
catch (Exception e) {
...
}
finally
{
...
}
空格
空⾏
空⾏增加了可读性。
空⾏把代码按内在相关逻辑分成块。
两个空⾏常⽤于:
l 源⽂件的逻辑区块之间
l 类与接⼝的定义之间(⽤每个⽂件定义⼀个类或者借⼝来避免这种情况)
⼀个空⾏常⽤于:
l ⽅法之间
l 属性之间
l ⽅法中的局部变量和该局部变量的第⼀个语句之间
l ⽅法中的逻辑模块之间,以增加可读性
注意:任何时候都应该保持正确的缩进。
缩进⽐空⾏更能够增加可读性。
保持正确的缩进,在VS2003中,只要选中所有的代码,
按CTRL+KF即可完成。
代码中的空格
逗号或者分号后⾯都应该有空格,例如:
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)
命名规范
⼤写规范
这个规范要求⼤写每个单词的⾸字母(如TestCounter)。
这个规范要求⼤写每个单词的⾸字母除了第⼀个单词,如testCounter。
仅仅对于由⼀个或两个字母的缩写组成的标识符,我们⽤upper case,那些由3个或更多字符组成的标识符应该⽤Pascal Casing.例如:
public class Math
{
public const PI = ...;
public const E = ...;
public const FeigenBaumNumber = ...;
}
命名准则
⼀般说来,在名字⾥使⽤下划线,并且根据匈⽛利的符号准则来命名都被视为不实⽤。
匈⽛利的符号是⼀组前后缀,⽤在变量名称⾥表⽰变量类型的。
在早先的Windows程序语⾔⾥⾯,就是⼴泛的使⽤这种命名⽅式的,但是现在已经舍弃了这种命名⽅式,或者说,⾄少⼤多数⼈反对使⽤它。
在这种新的准则下是不可以使⽤匈⽛利符号的。
⽽且要记住:⼀个好的变量名称描述的是变量的含义⽽⾮类型。
这种准则的⼀个反例就是GUI编码。
包含GUI元素的所有的域和变量的名称都加上了不缩写的类型名当后缀。
例如:
System.Windows.Forms.Button cancelButton;
System.Windows.Forms.TextBox nameTextBox;
l Namespace的命名规则⼀般如下:CompanyName.TechnologyName[.Feature][.Design]。
(例如:Microsoft.Media、
Microsoft.Media.Design)
l 使⽤⼀个稳定的,易识别的技术名称作为第⼆个名字。
如果需要为⼀个namespace定义设计时期的类型,那么需要重新定义⼀
个namespace包含这些类型,其名称就是在前⼀个namespace后加上.Design。
例如:System.Windows.Forms.Design Namespace 包含了设计器及相关的类⽤来设计System.Windows.Forms中的类。
l 使⽤Pascal⼤写⽅式,⽤点号分隔逻辑成分。
(例如,Microsoft.Office.PowerPoint)。
如果你的品牌使⽤的是⾮传统⼤写⽅式,那么⼀定要遵循你的品牌所确定使⽤的⼤写⽅式。
即使这种⽅式背离了通常的名称空间⼤写规则。
(例如,NeXT.WebObjects, and
ee.cummings.)
l 该⽤复数的时候要使⽤复数的名称空间名。
例如,使⽤System.Collections⽽不是使⽤System.Collection。
本规则的特例是品牌名称和缩写。
例如:使⽤use System.IO ⽽不是System.IOs
l namespace和类不能使⽤同样的名字。
例如,有⼀个namespace被命名为Debug后,就不要再使⽤Debug作为类名。
l namespace没有必要⼀定和assembly的名称相同。
⽐如assembly MyCompany.MyTechnology.dll并不意味着其中⼀定包含着MyCompany.MyTechnology namespace
类的命名准则
l 类名必须是名词或是名词短语
l 使⽤Pascal Casing (见8.1.1)
l 不要使⽤任何类型前缀(⽐如C)。
例如⽤FileStream⽽不是CFileStream来命名⼀个类
l 尽可能少在类名中使⽤缩写
l 不要使⽤连字号“_”
l 在必须的情况下,⼀个类名的开始字母可以为I,即使这个类不是Interface。
⽐如类名是IdentifyStore
l 在例外中,必须将Exception作为类的结尾。
例如: ApplicationException
l 在属性的后⾯加上Attribute后缀,来⾃定义Attribute类
接⼝命名准则
l 使⽤名词,名词短语,或者是表⽰⾏为的形容词来命名接⼝。
(例如:IComponent 或者IEnumberable)
l 使⽤Pascal Casing (见8.1.1)
l 名称前加I作为前缀,I后跟⼀个⼤写字母(接⼝名称的第⼀个字母)
l 尽可能少在接⼝名中使⽤缩写
l 不要使⽤连字号“_”
l 当类完成⼀个接⼝时,定义这⼀对类/接⼝组合就要使⽤相似的名称。
两个名称的不同之处只是接⼝名前有⼀个I前缀
下⾯我们举个例⼦,来看看接⼝IComponent和它的标准执⾏,类Component。
public interface IComponent
{
}
public class Component : IComponent
{
}
枚举变量的命名准则
l 对于枚举和枚举类型的变量的名称使⽤Pascal Casing
l 不给枚举类型或枚举变量添加前缀(或后缀)
l 尽可能少在枚举中使⽤缩写
l 枚举名称需使⽤单数名词。
在特殊情况下可以使⽤复数
只读和常量的命名
l 使⽤名词,名词短语,或者名词缩写来命名static fields
l 使⽤Pascal Casing (见8.1.1)
成员变量的命名
l 使⽤描述性的名称,该名称可以确定变量含义和类型,推荐以变量含义为基础的名称。
l 使⽤Pascal Casing (见8.1.1)
l 名称前加f作为前缀,f后跟⼀个⼤写字母。
(例如:fLength)
l 不要使⽤匈⽛利命名
参数
l 使⽤描述性的名称,该名称可以确定变量含义和类型。
推荐以参数含义为基础的名称。
l 使⽤Camel Casing (见8.1.2)
l 根据参数的意思来命名参数,⽽不是根据参数的类型来命名。
我们希望开发⼯具可以⽤很⽅便的⽅式提供关于参数种类的信息,这样参数名可以得到更好的使⽤,可以对语义⽽不是对类型进⾏描述。
但是偶尔使⽤根据类型命名的参数名也是完全可以的
l 不要⽤匈⽛利类型的符号作为参数名的前缀
变量命名
l 对于“平凡”的记数循环,记数变量推荐⽤I,j,k,l,m,n(见10.2关于全局变量更明智的命名⽅式的⽰例等等)
l 推荐对于类似Is,Hasor Can等boolean变量使⽤前缀,⼀般给boolean变量可以意味着对错的名称(例如:fileFound, done, success或者加前缀: isFileFound, isDone, isSuccess 但是不要使⽤IsName这种毫⽆意义的名称)
l 使⽤Camel Casing (见8.1.2)
的命名
l ⽤动词或者动词短语给method来命名
l ⽤Pascal Casing (见8.1.2)
属性的命名
l ⽤名词或名词短语命名属性
l ⽤Pascal Casing (见8.1.2)
l 可考虑⽤属性的类型名来命名它
l 不要使⽤匈⽛利命名
事件的命名
l 在delegate定义时,⽤EventHandler作为后缀
l 使⽤名为sender和e的两个参数。
sender参数代表发出这个事件的对象。
sender参数永远是⼀个类型对象,即使它可能使⽤了更为特定的类型。
与事件相关的状态被封装在⼀个名为e的事件类型中。
l 使⽤Pascal Casing (见8.1.1)
l 在定义事件参数类型的时候,需要加上EventArgs后缀
l 使⽤现在进⾏时来命名⼀个即将被执⾏的事件,⽤过去时表⽰⼀个执⾏后的事件。
例如,⽤Closing和Closed来命名事件。
不要
⽤BeforeXxx/AfterXxx来命名事件
l 不要给事件名加前缀和后缀。
例如,⽤Close⽽不是OnClose
l 可考虑⽤动词或者动词短语定义事件名称
l 通常情况下,需要给⼀个protected virtual的⽅法⽤来调⽤Event,这个⽅法⽤OnXxx命名,这样⼦类就可以对这个⽅法进⾏override。
这个⽅法有且仅有⼀个参数e,⽽sender通常⽤this来赋值
⼤⼩写总结
Type Case Notes
Class / Struct Pascal Casing
Interface Pascal Casing以I开始
Enum values Pascal Casing
Enum type Pascal Casing
Events Pascal Casing
Exception class Pascal Casing以Exception结束
Attribute class Pascal Casing以Attribute结束
public Fields Pascal Casing很少使⽤,通常⽤属性
Methods Pascal Casing
Namespace Pascal Casing
Property Pascal Casing
Field Pascal Casing以f开始
Type Case Notes
Parameters Camel Casing
程序设计惯例
可见度
任何时候都不要将实例变量声明为public,要使⽤private关键字。
尽量使⽤属性或者使⽤public const或者public static readonly来替代。
常量
除了0、-1、1等常⽤数值外,使⽤的数值都要声明为常量(例如,应⽤程序可以处理3540个⽤户。
如果“3540”采⽤硬编码的⽅式分散
在25000⾏代码中的50⾏中,总共427处。
如果现在改为3800个,所有的值都要修改,很难保证每个值都能被正确的修改。
)。
不要使⽤魔法数字,例如:把常数数值直接放⼊源代码。
过后如果变化(⽐如,应⽤程序现在可以处理3540个⽤户⽽不是分散
于25000LOC的50⾏编码中的427个hardcoded)易出错的和徒然的,则这些数值要被更改。
我们⽤下⾯这种⽅式来定义有常数值的常量:public class MyMath
{
public const double PI = 3.14159...
}
编码实例
⼤括号的实例
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 }
}
}
}
括号应该另起⼀⾏。
变量命名⽰例
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");
}
}
⽤智能命名代替上⾯的:
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.");
}
}
注:Indexer 变量⼀般⽤i, j, k等等。
但是在上⾯这种情况下,重新考虑这种原则还是有意义的。
总之,当同⼀个counter或者indexer被重复使⽤的时候,要给它们⼀个有意义的名称。