2.3 C#代码编写规范

代码是软件开发过程的产物,其作用是通过编译器编译后运行,以达到预期的效果(功能、稳定性、安全性等),而另外一个重要作用是给人阅读。对于机器来说,只要代码正确就能够正确地运行程序,但是人不同,如果代码编写混乱就会对代码阅读造成障碍,导致代码无法维护,甚至会导致代码重构等高成本活动,所以规范代码势在必行。

2.3.1 命名规范

命名规范主要涉及命名空间、类型、接口、属性、方法、变量等相关命名。它在代码编写中起到很重要的作用,不仅可以清晰地表达出程序规划的核心,还可以达到让人“望文知意”的效果。虽然不遵循命名规范程序也可以运行,但是使用命名规范可以很直观地了解代码所代表的含义。下面列出来一些常用的命名规范。

1. 字母大小写约定

1)Pascal风格

将标识符的首字母和后面连接的每个单词的首字母都大写。可以对三个字符或更多字符的标识符使用Pascal大小写。

例如:BackColor、HelloWorld和SetName等。

2)Camel风格

标识符的首字母小写,而每个后面连接的单词的首字母都大写。

例如:backColor、productId等。

3)大写规范

标识符中的所有字母都大写。仅对于由两个或者更少字母组成的标识符使用该约定。

例如:System.IO、System.Web.UI等。

有时候大写标识符必须维持与现有非托管符号方案的兼容性,在该方案中所有大写字母经常用于枚举和常数值。

表2-1汇总了字母大小写约定,并提供了不同类型的标识符的实例。

表2-1 字母大小写约定

其实字母大小写约定很容易理解,一般情况下都是Pascal风格,除了方法的参数是使用Camel风格。微软的类库都是严格遵守这些规则的,用户在编写代码时,如果不知道是用Pascal还是Camel,只要看一下微软的智能提示就可以。

2. 区分大小写

为了避免混淆和保证跨语言交互操作,请遵循有关区分大小写的使用的下列规则。

(1)不要使用要求区分大小写的名称。对于区分大小写和不区分大小写的语言,组件都必须完全可以使用。不区分大小写的语言无法区分同一上下文中仅大小写不同的两个名称。因此,在创建的组件或类中必须避免这种情况。

(2)不要创建仅是名称大小写有区别的两个命名空间。例如,不区分大小写的语言无法区分以下两个命名空间声明。

   namespace ee.cummings;
   namespace Ee.Cummings;

(3)不要创建具有仅是大小写有区别的参数名称的函数。下面的实例是不正确的。

   void MyFunction(string a, string A)

(4)不要创建仅是大小写有区别的类型名称的命名空间。在下面的实例中,Point p和POINT p是不适当的类型名称,原因是它们仅在大小写方面有区别。

   System.Windows.Forms.Point p
   System.Windows.Forms.POINT p

(5)不要创建仅是大小写有区别的属性名称的类型。在下面的实例中,int Color和int COLOR是不适当的属性名称,原因是它们仅在大小写方面有区别。

   int Color {get, set}
   int COLOR {get, set}

3. 缩写

为了避免混淆和保证跨语言交互操作,请遵循有关缩写的使用的下列规则。

(1)不要将缩写或缩略形式用作标识符名称的组成部分。例如,使用GetWindow,而不要使用GetWin。

(2)不要使用计算机领域中未被普遍接受的缩写。

(3)在适当的时候,使用众所周知的缩写替换冗长的词组名称。例如,用UI作为User Interface的缩写,用OLAP作为On-line Analytical Processing的缩写。

(4)在使用缩写时,对于超过两个字符长度的缩写,请使用Pascal大小写或Camel大小写。例如,使用HtmlButton或htmlButton。但是,应当大写仅有两个字符的缩写,如System.IO,而不是System.Io。

(5)不要在标识符或参数名称中使用缩写。如果必须使用缩写,对于由多于两个字符所组成的缩写请使用Camel大小写,虽然这和单词的标准缩写相冲突。

2.3.2 namespace的命名规范

namespace的命名一般是使用公司名称,后跟技术名称和可选的功能与设计,如下所示。

   CompanyName.TechnologyName[.Feature][.Design]

例如:

   Microsoft.Media
   Microsoft.Media.Design
   NESC.Data.SQLHelper

namespace的命名规范说明如下。

(1)给命名空间名称加上公司名称或者其他知名商标的前缀可以避免两个已经发布的命名空间名称相同的可能性。

例如:

Microsoft.Office是由Microsoft提供的Office Automation Classes的一个适当的前缀。

(2)在第二级分层名称上使用稳定的、公认的技术名称。将组织层次架构用作命名空间层次架构的基础。命名一个命名空间,该命名空间包含为具有.Design后缀的基命名空间提供设计时功能的类型。

例如:

System.Windows.Forms.Design命名空间包含用于设计基于System.Windows.Forms的应用程序的设计器和相关的类。

(3)嵌套的命名空间应当在包含它的命名空间中的类型上有依赖项。

例如:

System.Web.UI.Design中的类依赖于System.Web.UI中的类。但是,System.Web.UI中的类不依赖于System.Web.UI.Design中的类。

(4)应当对命名空间使用Pascal大小写,并用句点分隔逻辑组件,如Microsoft.Office.PowerPoint中所示。如果用户的商标使用非传统的大小写,请遵循商标所定义的大小写,即使它与规定的Pascal大小写相背离。

例如:

命名空间NeXT.WebObjects和ee.cummings阐释了对于Pascal大小写规则的适当背离。

(5)如果在语义上适当,使用复数命名空间名称。

例如:

使用System.Collections而不是System.Collection。此规则的例外是商标名称和缩写。

使用System.IO而不是System.IOs。

(6)不要为命名空间和类使用相同的名称。例如,不要既提供Debug命名空间又提供Debug类。最后,请注意命名空间名称不必非得与程序集名称相似。

2.3.3 类的命名规范

以下规则概述命名类的规范。

(1)使用名词或名词短语命名类。

(2)使用Pascal大小写。

(3)少用缩写。

(4)不要使用类型前缀,如在类名称上对类使用C前缀。例如,使用类名称FileStream,而不是CFileStream。

(5)不要使用下画线字符(_)。

(6)有时候需要提供以字母I开始的类名称,虽然该类不是接口。只要I是作为类名称组成部分的整个单词的第一个字母,这便是适当的。例如,类名称IdentityStore就是适当的。

(7)在适当的地方,使用复合单词命名派生的类。派生类名称的第二个部分应当是基类的名称。例如,ApplicationException对于从名为Exception的类派生的类是适当的名称,原因是ApplicationException是一种Exception。

下面是正确命名的类的实例:

   public class FileStream
   public class Button
   public class String

2.3.4 接口的命名规范

以下规则概述接口的命名规范。

(1)用名词或名词短语,或者描述行为的形容词命名接口。例如,接口名称IComponent使用描述性名词。接口名称ICustomAttributeProvider使用名词短语。名称IPersistable使用形容词。

(2)使用Pascal大小写。

(3)少用缩写。

(4)给接口名称加上字母I前缀,以指示该类型为接口。

(5)在定义类/接口对(其中类是接口的标准实现)时使用相似的名称。两个名称的区别应该只是接口名称上有字母I前缀。

(6)不要使用下画线字符(_)。

以下是正确命名的接口的实例:

   public interface IServiceProvider
   public interface IFormatable

2.3.5 方法的命名规范

以下规则概述方法的命名规范。

(1)使用动词或动词短语命名方法。

(2)使用Pascal大小写。

以下是正确命名的方法的实例。

2.3.6 不同变量使用前缀区分

对于private的变量,使用以下约定。

(1)成员变量应该在开始就被声明,并集中放在class的开始位置。

(2)请使用m,s前缀来区分成员变量和静态成员变量。对于局部变量不适用任何前缀。

例如:

2.3.7 注释规范

注释用来对编写的代码进行说明,包括功能说明以及实现说明,这样可以大大提高程序的可读性,另外,规范的注释还可以通过工具来生成相应的API文档,C#的注释规范有以下几种。

1. 类注释规范

类模块开始必须按以下形式书写模块注释:

如果模块有修改,则每次修改必须添加以下注释:

2. 类属性注释规范

在类的属性中必须按以下格式编写属性注释:

3. 方法注释规范

在类的方法声明前必须按以下格式编写注释:

4. 代码间注释规范

代码间注释分为单行注释和多行注释。

1)单行注释

   //<单行注释>

2)多行注释

   /*多行注释1
   *多行注释2
   *多行注释3*/

注释说明:

(1)代码中遇到语句块时必须添加注释,并且添加的注释必须能够说明此语句块的作用和实现手段。

(2)尽量多点儿注释,就算能一目了然的命名最好也顺便写一些注释,以方便以后接手的人能更容易理解程序。

(3)如果因为某种原因使用了复杂艰涩的原理,应为程序配备良好的文档和更多的注释。

2.3.8 布局规范

布局规范的目的是使代码变得整洁,提高代码可读性,其主要规范如下。

1. 列宽

每行代码列宽应该控制在110字符左右。

2. 换行

当表达式超出或即将超出规定的列宽时,遵循以下规则进行换行。

(1)在逗号后换行。

(2)在操作符前换行。

3. 缩进

缩进应该是每行一个Tab键或者4个空格。左右花括号必须独自一行,括号内容为空时除外。

例如:

4. 空行

空行是为了将逻辑上相关联的代码分块,以便提高代码的可阅读性。在以下情况下使用两个空行。

(1)接口和类的定义之间。

(2)枚举和类的定义之间。

(3)类与类的定义之间。

在以下情况下使用一个空行。

(1)方法与方法、属性与属性之间。

(2)方法中变量声明与语句之间。

(3)方法与方法之间。

(4)方法中不同的逻辑块之间。

(5)方法中的返回语句与其他的语句之间。

(6)属性与方法、属性与字段、方法与字段之间。

(7)注释与它注释的语句间不空行,但与其他的语句间空一行。

5. 空格

在以下情况中要使用到空格。

(1)关键字和左括号“(”之间应该用空格隔开,如while(true)。

注意:在方法名和左括号“(”之间不要使用空格,这样有助于辨认代码中的方法调用与关键字。

(2)多个参数用逗号隔开,每个逗号后都应加一个空格。

(3)除了“.”之外,所有的二元操作符都应用空格与它们的操作数隔开。一元操作符、自增自减运算符与操作数间不需要空格。

例如:

(4)语句中的表达式之间用空格隔开。

例如:

   for (expr1; expr2; expr3)

6. 括号“()”

(1)左括号“(”不要紧靠关键字,中间用一个空格隔开。

(2)左括号“(”与方法名之间不要添加任何空格。

(3)没有必要的话不要在返回语句中使用括号。

7. 花括号“{}”

(1)左花括号“{”放于关键字或方法名的下一行并与之对齐。

例如:

   if (condition)
   { 
   }

(2)左花括号“{”要与相应的右花括号“}”对齐。

(3)通常情况下,左花括号“{”单独成行,不与任何语句并列一行。

(4)if、while、do语句后一定要使用“{}”,即使“{}”中为空或只有一条语句。

(5)右花括号“}”后建议加一个注释以便于方便地找到与之相应的“{”。

例如: