C#程序书写规范

C#程序书写规范


 

1. 关于C# 型代码指南. 4

2. 文件系统(文件结构). 4

2.1 C# 源文件. 4

2.2路径布局. 4

3. 缩进. 4

3.1 wrapping lines 4

3.2 空格. 5

4. 注释. 5

4.1 块注释. 5

4.2 单行注释. 5

4.3 文件注释. 5

5 定义变量. 6

5.1每行定义变量的个数. 6

5.2初始化. 6

5.3类,接口和namespace的定义. 7

5.4定义method 7

5.4定义属性,索引器和事件. 7

6 语句. 8

6.1简单语句. 8

6.2 Return语句. 8

6.3 If, if-else, if else-if else 语句. 8

6.4 For/Foreach语句. 9

6.5 While/do-while 语句. 10

6.6 Switch 语句. 10

6.7 Try-catch 语句. 10

7 空格. 11

7.1空行. 11

7.2代码中的空格. 12

8 命名规范. 12

8.1大写规范. 12

8.1.1 Pascal casing 12

8.1.2 Camel casing 12

8.1.3 Upper case 12

8.2命名准则. 12

8.2.1 Namespace 13

8.2.2类的命名准则. 13

8.2.3接口命名准则. 14

8.2.4枚举变量的命名准则. 14

8.2.5只读和常量的命名. 14

8.2.6成员变量的命名. 14

8.2.7参数. 15

8.2.8变量命名. 15

8.2.9 Method的命名. 15

8.2.10属性的命名. 15

8.2.11事件的命名. 15

8.2.12 大小写总结. 16

9程序设计惯例. 17

9.1可见度. 17

9.2常量. 17

10编码实例. 17

10.1大括号的实例. 17

10.2变量命名示例. 18

 


 

1. 关于C# 型代码指南

这个文档是编写稳定可靠的程序的指南。该文章主要着眼于用C# 编写的程序,但其中许多的规则和原理甚至在使用其他编程语言时也十分有用。

2. 文件系统(文件结构)

2.1 C# 源文件

尽可能让类名或者文件名短小,不要超过2000个字符。把每一个类作为一个单独的文件,并且用类的名字来命名该文件(当然要加后缀名.cs)。这个约定可以使得命名更加容易。

 

2.2路径布局

给每一个namespace创建一个路径。(列如 MyProject/TestSuite/TestTier作为文件MyProject.TestSuite.TestTier的路径,不要使用namespace名字中的点)。这样可以更容易规划namespace的路径。

3. 缩进

3.1 wrapping lines

当一个表述无法用一行写完时,依照下列常用规则把它分段:

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;

第一个是标准的,由于断点出现在加上括弧的表达式外边(高级准则)。

3.2 空格

标准的使用空格作为行首缩进没有固定的格数。有些人喜欢2个空格,有些喜欢4个空格,甚至有些是8个或者更多的空格。

现在规定,使用VS.NET默认的4个空格作为缩进。在缩进的时候,更多的希望使用空格而不是TAB作为缩进。

 

4. 注释

4.1 块注释

通常我们避免使用块注释。推荐用///的形式来给出标准的C#注释。若要使用块注释,那么其形式如下:

    /* 第一行

     *第二行

     *第三行

   */

这样对于读者来说,块就从代码变成了可读的文档。相类似的,对于单行的注释你可以使用这种老式的C语言的注释方式,尽管这个是不推荐使用的。但是在这种注释方式下,就必须根据注释内容来分行,否则就无法通过同一行的注释了解代码的运行了:

   /* blah blah blah */

块注释只在少数几种情况下才有用,例子参见TechNoteThe fine Art of Commenting”。一般来讲,对于段落代码使用块注释很有效。

4.2 单行注释

使用//形式来注释单行代码(VS.NET对于单行注释有快捷键,CTRL+KC)。它也可以用来给段落代码加注释。对代码文件使用单行的注释时,必须根据缩进级别来缩进。被注释的代码必须在第一行中加以注释,由此增加被注释代码的可读性。一般注释的长度不可以比编码解释长很多。

4.3 文件注释

.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文件。

5 定义变量

5.1每行定义变量的个数

推荐每行定义一个变量,这样便于注释。换句话说,

int level; // indentation levelint size; // size of table

定义变量时,不要在一行中放入多个变量或者是不同类型的变量。

例如:

int a, b; //'a'是什么? 'b'代表什么?

这个例子也显示了变量名字不显然的弊端。命名变量时一定要清楚这点。

5.2初始化

局部变量在定义的时候就要初始化。例如:

string name = myObject.Name;

或者

int val = time.Hours;

注:如果要初始化一个对话框,同时需要释放资源。要使用using 语句:

using (OpenFileDialog openFileDialog = new OpenFileDialog())

{

//…

}

5.3类,接口和namespace的定义

当编译C# 的类,接口和namespace的时候,必须按照一下几个初始化原则:

l         左大括号“{”出现与定义语句的下一行

l         右大括号“}”单独成一行,与相应的左大括号配套。

例如:

class MySample : MyClass, ImyInterface

{

int myInt;

}

namespace Mynamespace

{

// Namespace contents

}

关于大括号放置的示例参看10.1节。

5.4定义method

关于定义method,要应用括号放置原则,而且在method的名字跟括号“(”之间不可以有空格,“(”后边跟参数表。

例如:

public MySample(int myInt)

{

this.myInt = myInt;

}

void Inc()

{

++myInt;

}

5.4定义属性,索引器和事件

对于属性,indexer和事件的定义,在定义的同行不可以用任何括号。例如:

public int Amount

{

get

{

...

}

set

{

...

}

}

 

public this[string index]

{

get;

set;

}

public EventHandler MyEvent

{

add

{

...

}

remove

{

...

}

}

 

6 语句

6.1简单语句

每一行仅有一个语句。

6.2 Return语句

Return语句使用时,外面不可以加圆括号。

不用:return (n*n+1/2) ;

 return n*(n+1)/2 ;

6.3 If, if-else, if else-if else 语句

   If , if-else, 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语句为:

try

{

...

}

catch (Exception)

{

}

或者

try

{

    ...

}

catch (Exception e)

{

    ...

}

或者

try

{

    ...

}

catch (Exception e)

{

    ...

}

finally

{

    ...

}

7 空格

7.1空行

空行增加了可读性。空行把代码按内在相关逻辑分成块。两个空行常用于:

l         源文件的逻辑区块之间

l         类与接口的定义之间(用每个文件定义一个类或者借口来避免这种情况)

一个空行常用于:

l         方法之间

l         属性之间

l         方法中的局部变量和该局部变量的第一个语句之间

l         方法中的逻辑模块之间,以增加可读性

注意:任何时候都应该保持正确的缩进。缩进比空行更能够增加可读性。保持正确的缩进,在VS2003中,只要选中所有的代码,按CTRL+KF即可完成。

7.2代码中的空格

逗号或者分号后面都应该有空格,例如:

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)

8 命名规范

8.1大写规范

8.1.1 Pascal casing

这个规范要求大写每个单词的首字母(如TestCounter)。

8.1.2 Camel casing

这个规范要求大写每个单词的首字母除了第一个单词,如testCounter

8.1.3 Upper case

仅仅对于由一个或两个字母的缩写组成的标识符,我们用upper case,那些由3个或更多字符组成的标识符应该用Pascal Casing.例如:

public class Math

{

     public const PI = ...;

     public const E = ...;

     public const FeigenBaumNumber = ...;

}

8.2命名准则

一般说来,在名字里使用下划线,并且根据匈牙利的符号准则来命名都被视为不实用。匈牙利的符号是一组前后缀,用在变量名称里表示变量类型的。在早先的Windows程序语言里面,就是广泛的使用这种命名方式的,但是现在已经舍弃了这种命名方式,或者说,至少大多数人反对使用它。在这种新的准则下是不可以使用匈牙利符号的。而且要记住:一个好的变量名称描述的是变量的含义而非类型。这种准则的一个反例就是GUI编码。包含GUI元素的所有的域和变量的名称都加上了不缩写的类型名当后缀。例如:

System.Windows.Forms.Button cancelButton;

System.Windows.Forms.TextBox nameTextBox;

8.2.1 Namespace

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

 

8.2.2类的命名准则

l         类名必须是名词或是名词短语

l         使用Pascal Casing (见8.1.1

l         不要使用任何类型前缀(比如C)。例如用FileStream而不是CFileStream来命名一个类

l         尽可能少在类名中使用缩写

l         不要使用连字号“_

l         在必须的情况下,一个类名的开始字母可以为I,即使这个类不是Interface。比如类名是IdentifyStore

l         在例外中,必须将Exception作为类的结尾。例如: ApplicationException

l         在属性的后面加上Attribute后缀,来自定义Attribute

8.2.3接口命名准则

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

{

}

8.2.4枚举变量的命名准则

l         对于枚举和枚举类型的变量的名称使用Pascal Casing

l         不给枚举类型或枚举变量添加前缀(或后缀)

l         尽可能少在枚举中使用缩写

l         枚举名称需使用单数名词。在特殊情况下可以使用复数

8.2.5只读和常量的命名

l         使用名词,名词短语,或者名词缩写来命名static fields

l         使用Pascal Casing (见8.1.1

8.2.6成员变量的命名

l         使用描述性的名称,该名称可以确定变量含义和类型,推荐以变量含义为基础的名称。

l         使用Pascal Casing (见8.1.1

l         名称前加f作为前缀,f后跟一个大写字母。(例如:fLength

l         不要使用匈牙利命名

 

8.2.7参数

l         使用描述性的名称,该名称可以确定变量含义和类型。推荐以参数含义为基础的名称。

l         使用Camel Casing (见8.1.2

l         根据参数的意思来命名参数,而不是根据参数的类型来命名。我们希望开发工具可以用很方便的方式提供关于参数种类的信息,这样参数名可以得到更好的使用,可以对语义而不是对类型进行描述。但是偶尔使用根据类型命名的参数名也是完全可以的

l         不要用匈牙利类型的符号作为参数名的前缀

8.2.8变量命名

l         对于“平凡”的记数循环,记数变量推荐用I,j,k,l,m,n(见10.2关于全局变量更明智的命名方式的示例等等)

l         推荐对于类似Is,Hasor Canboolean变量使用前缀,一般给boolean变量可以意味着对错的名称(例如:fileFound, done, success或者加前缀: isFileFound, isDone, isSuccess 但是不要使用IsName这种毫无意义的名称)

l         使用Camel Casing (见8.1.2

8.2.9 Method的命名

l         用动词或者动词短语给method来命名

l         Pascal Casing (见8.1.2

8.2.10属性的命名

l         用名词或名词短语命名属性

l         Pascal Casing (见8.1.2

l         可考虑用属性的类型名来命名它

l         不要使用匈牙利命名

8.2.11事件的命名

l         delegate定义时,用EventHandler作为后缀

l         使用名为sendere的两个参数。sender参数代表发出这个事件的对象。sender参数永远是一个类型对象,即使它可能使用了更为特定的类型。与事件相关的状态被封装在一个名为e的事件类型中。

l         使用Pascal Casing (见8.1.1

l         在定义事件参数类型的时候,需要加上EventArgs后缀

l         使用现在进行时来命名一个即将被执行的事件,用过去时表示一个执行后的事件。例如,用ClosingClosed来命名事件。不要用BeforeXxx/AfterXxx来命名事件

l         不要给事件名加前缀和后缀。例如,用Close而不是OnClose

l         可考虑用动词或者动词短语定义事件名称

l         通常情况下,需要给一个protected virtual的方法用来调用Event,这个方法用OnXxx命名,这样子类就可以对这个方法进行override。这个方法有且仅有一个参数e,而sender通常用this来赋值

 

 

8.2.12 大小写总结

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

 

 

9程序设计惯例

9.1可见度

任何时候都不要将实例变量声明为public,要使用private关键字。尽量使用属性或者使用public const或者public static readonly来替代。

 

9.2常量

除了0-11等常用数值外,使用的数值都要声明为常量(例如,应用程序可以处理3540个用户。如果“3540”采用硬编码的方式分散在25000行代码中的50行中,总共427处。如果现在改为3800个,所有的值都要修改,很难保证每个值都能被正确的修改。)。

不要使用魔法数字,例如:把常数数值直接放入源代码。过后如果变化(比如,应用程序现在可以处理3540个用户而不是分散于25000LOC50行编码中的427hardcoded)易出错的和徒然的,则这些数值要被更改。我们用下面这种方式来定义有常数值的常量:

public class MyMath

{

public const double PI = 3.14159...

}

 

10编码实例

10.1大括号的实例

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

              }

         }

     }

}

 

括号应该另起一行。

 

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");

              }

         }

用智能命名代替上面的:

         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被重复使用的时候,要给它们一个有意义的名称。

 

posted @ 2007-03-07 20:59  Winner.Net(2007)  阅读(2637)  评论(0编辑  收藏  举报