asp.net开发人员手册 昨天刚整理完

目录

1    导言    4

1.1    目的    4

1.2    适用范围    4

1.3    术语定义    4

1.4    参考资料    4

1.5    版本更新记录    4

2    使用的开发工具:    5

3    注释规范    5

3.1    模块注释    5

3.2    类的注释    6

3.3    类的成员方法注释    7

3.4    类成员、变量、属性的注释    9

3.5    程序注释    10

1.    命名规范    10

3.6    大写规则    11

3.7    区分大小写    12

3.8    缩写    13

3.9    措词    14

3.10    避免类型名称混淆    15

3.11    命名空间命名规范    16

4    类命名规范    17

4.1    接口命名规范    18

4.2    属性命名规范    19

4.3    枚举类型命名规范    19

4.4    静态字段命名规范    20

4.5    参数命名规范    20

4.6    方法命名规范    20

4.7    属性命名规范    21

4.8    变量命名    22

4.9    事件命名规范    22

5    代码书写规范    24

5.1    排版规范    24

6    类成员使用规范    28

6.1    属性使用指南    28

只读和只写的属性    30

6.2    事件使用指南    30

6.3    方法使用指南    31

6.3.1    方法重载指南    31

6.4    构建函数使用指南    32

6.5    类的成员变量使用指南    33

6.6    参数名称指南    34

7    类型使用指南    35

7.1    类使用指南    35

7.1.1    基类使用指南    35

7.2    值类型使用指南    36

7.2.1    结构使用指南    36

7.2.2    枚举使用指南    37

7.3    程序代理使用指南    38

7.4    程序属性(Attribute)的使用    38

8    异常的产生和处理    38

8.1    标准异常类型    41

8.2    异常的包装Wrapping Exceptions    42

9    数组使用指南    42

9.1    数组vs. 集合    42

9.1.1    集合    42

9.1.2    集合中可索引的属性    43

9.1.3    数组值属性    43

9.2    返回空数组    43

10    前台控件命名方法    43

10.1    主要控件名简写对照表    43

11    数据库设计开发规范    44

11.1    命名规范    44

11.1    表结构设计遵守3NF 标准3NF 规定:    44

11.2    字段设计要求    45

11.3    视图使用原则    45

11.4    存储过程建立规则    45

11.5    函数建立规则    46

11.6    触发器使用要求    46

11.7    注释    46

1    附录一: 匈牙利命名法    47

导言

目的

本文档是为基于Microsoft®公司的Visual Studio .Net® - C#.Net系列开发工具进行软件开发的一个规范性文件。其目的是:

  • 保证软件开发过程中有一个统一的标准可以进行参照:包括类库、公共接口、设计模式、命名规范,数据库编程格式等。 
  • 能够合理的使用公用资源

适用范围

本文档主要是基于目前三层架构开发模式进行描述的:Asp.net/BusinessIntegration/Database。

术语定义

    匈牙利命名法是一种编程时的命名规范。基本原则是:变量名=属性+类型+对象描述,其中每一对象的名称都要求有明确含义,可以取对象名字全称或名字的一部分。命名要基于容易记忆容易理解的原则。

参考资料

  1. 《C# Language Specification.doc》

版本更新记录

版本/修订版

修改确认日期

修改内容概述

起草人

审核人

备注

1.0 

2005-10

初始版本

孙同海

   

2.0

2011-4-26

海天修改

陈凡民

孙同海,李进

 
           

 

使用的开发工具:

  • Microsoft® Visual Studio .Net 2008® - C#.Net

注释规范

要使程序易于理解,注释十分重要。在此,提供基本的注释编写规范。

  • 边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。
  • 注释的内容要清楚、明了,含义准确,防止注释二义性。
  • 避免在注释中使用缩写,特别是非常用缩写。
  • 注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。变量注释和变量在同一行,所有注释必须对齐,与变量分开至少两个Tab键。程序段或语句的注释在程序段或语句的上一行。
  • 注释与所描述内容进行同样的缩排。
  • 重要变量必须有注释。
  • 典型算法必须有注释。
  • 在循环和逻辑分支的地方必须写上注释。
  • 避免在一行代码或表达式的中间插入注释

传统的注释风格:单行注释符号//和多行注释符号/* */在C#中依然可以使用。具体的用法可以参考C语言中的规范。

模块注释

在一个程序模块的开始,应用注释说明模块的名字、功能、开发者和日期和版本变更历史,也可以将部分思路写到注释,不做要求,如下所示:

 

//-------------------------------------------------------------------

//Name:    ChargeUser

//Function:    Charge credits from the user account and save it to the user،¯s account

//Author:    Author Name

// Date:        Aug. 8, 2000

//-------------------------------------------------------------------

//Change History:

// Date        Who        Changes Made

//-------------------------------------------------------------------

//2000-5-1    Author1        Initial creation

//2000-5-15    Author2        Add bStatus variable to identify

//2000-7-3    Author3    Add CheckBlackList() to check whether user

//        is in black list

//-------------------------------------------------------------------

类的注释

在定义一个类之前,应用"///"注释说明类的功能、使用方法和特殊的属性,如下所示:

/// <summary>

/// This class contains the business facade for the order system.

/// <remarks>

/// The business facade is used to provide a simplified interface into the

/// order sub systems.

/// </remarks>

///

/// <remarks>

/// This class is marked as MarshalByRefObject to support remoted scenarios.

/// </remarks>

/// </summary>

public class OrderSystem : MarshalByRefObject

{

}

须注意的是,我们要遵从VS.NET对注释的约定,使用<summary>标记来指定类总体注释的开始,用</summary>标记它的结束。对类的一些注解说明可以放在<remarks> 和 </remarks>对中。以后我们可以从这些注解自动得到对代码的说明文档。如下例所示:

Duwamish7.BusinessFacade.OrderSystem Class

This class contains the business facade for the order system. The business facade is used to provide a simplified interface into the order sub systems. This class is marked as MarshalByRefObject to support remoted scenarios.

Access: Public

Base Classes: MarshalByRefObject

  

Members

Description

  

  

  

GetOrderSummary

Fills in the order summary data for an order. The order that is being worked on. Updated OrderData object. This is returned to support the remoted scenario (OrderData is a MarshalByValue object). class='System. ApplicationException'> The order is null.

  

  

  

AddOrder

Add an order and return the transaction id. The order that is being worked on. The order that has just been added. class='System. ApplicationException'> The order is null.

  

Remarks:

The business facade is used to provide a simplified interface into the order sub systems.

This class is marked as MarshalByRefObject to support remoted scenarios.

类的成员方法注释

在定义类成员方法前,应说明该过程/函数的名字、功能、输入/输出和版本变更历史,如下所示:

/// <summary>

/// Fills in the order summary data for an order.

/// <param name="order">The order that is being worked on.</param>

/// <retvalue>Updated OrderData object.

/// <remarks>

/// This is returned to support the remoted scenario (OrderData is a

/// MarshalByValue object).

/// </remarks>

/// </retvalue>

/// </summary>

//-------------------------------------------------------------------

// Change History:

//Date            Who        Changes Made

// 2000-5-1        Author1        Initial creation

// 2000-5-15        Author2        Add bStatus variable to identify User،¯s state

//-------------------------------------------------------------------

public OrderData GetOrderSummary(OrderData order)

{

}

在这里"<param name="<参数名>">" 和"</param>"指明方法的参数的描述信息;"<retvalue>" 和"</retvalue>"对说明了方法返回值的特性。

由这些注解自动得到的说明文档如下所示:

Duwamish7.BusinessFacade.OrderSystem.GetOrderSummary Function

Fills in the order summary data for an order. The order that is being worked on. Updated OrderData object. This is returned to support the remoted scenario (OrderData is a MarshalByValue object).

Public OrderData GetOrderSummary (OrderData)

  

Type 

Name 

Description 

  

  

  

OrderData 

order 

The order that is being worked on. 

  

  

Return 

Description 

  

  

  

OrderData 

  

  

Remarks:

This is returned to support the remoted scenario (OrderData is a MarshalByValue object).

    在.NET中,提供了额外的XML文档注释标记(Tags for Documentation Comments)。这些标记包括如下表的全部内容:

<c>

<para>

<see>1

<code>

<param>1

<seealso>1

<example>

<paramref>1

<summary>

<exception>1

<permission>1

<value>

<include>1

<remarks>

<list>

<returns>

        

    在对类的定义描述中,至少要包含标记<summary>,其中的内容包括:

  • 类定义的简单描述
  • 该类引用的命名空间
  • 作者
  • 编辑日期
  • 修改日期
  • 修改原因

示例如下:

在类的内部,对于方法的声明,要求注释应该至少包含<summary>和<param>这两个标记。示例如下:

类成员、变量、属性的注释

在定义类成员属性前,应描述该属性,如:

对于模块级的常量、变量应该象类成员属性一样写注释。

程序注释

在代码实现时,应对其目的和实现的功能进行说明,其要点是:

  1. 采用了特殊的语法需要对语法注释;
  2. 为实现部分功能需要注释;
  3. 不能通过上下文立即明白其功能需要注释。

如下例所示:

public OrderData GetOrderSummary(OrderData order)

{

//

// Check preconditions

//

ApplicationAssert.CheckCondition(order != null, "Order is required", ApplicationAssert.LineNumber);

//

// Calculate subTotal

//

Decimal subTotal = 0;

……

  1. 命名规范

命名是编程的核心。能够对变量和函数/过程进行表意清晰而准确的命名,就能使程序的可读性大大提高,达到不说自明的效果。真正的名称是深入认真思考一个对象的生态环境后才能给出的。程序设计人员只有在充分理解并把握整个系统时,才可能给出真正合适的名字。如果名称选用恰当,一切就显得很自然,各部分关系清晰,意义可以推导而出,阅读程序时可以按常识推理,从而减小程序员对已有程序的阅读和理解困难,提高工作效率,使新程序员能在尽量短的时间内进入角色。

大写规则

使用下面的三种标识符约定:

  • Pascal 大小写规则

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

使用TextColor就比 TextcolorText_color更好.

注意不要大写 "连接词"(一个单词中包含了几个单词,但这个单词本身有自己的意思,如Checkbook)每个组合单词的首字母。应该将这个单词作为一个单词来考虑,而非几个单词的组合。使用词典决定一个组合词是不是应该作为一个单词来使用。

  • Camel 大小写规则

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

这种风格除了第一个单词的首字母,其他单词都应大写首字母,如下所示

backColor

在局部变量参数名或私有类属性名称上使用camel风格。

  • 大写规则

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

例如:System.IO

System.Web.UI

可能还必须大写标识符以维持与现有非托管符号方案的兼容性,在该方案中所有大写字母经常用于枚举和常数值。一般情况下,在使用它们的程序集之外这些字符应当是不可见的。

下表汇总了大写规则,并提供了不同类型的标识符的示例。

标识符

大小写规则

示例

Pascal 

AppDomain

枚举类型

Pascal 

ErrorLevel

枚举值

Pascal 

FatalError

事件

Pascal 

ValueChange

异常类

Pascal 

WebException

注意   总是以 Exception 后缀结尾。

只读的静态字段

Pascal 

RedValue

接口

Pascal 

IDisposable

注意   总是以 I 前缀开始。

方法

Pascal 

ToString

命名空间

Pascal 

System.Drawing

参数

Camel 

typeName

属性

Pascal 

BackColor

受保护的实例字段

Camel 

redValue

注意   很少使用。属性优于使用受保护的实例字段。

公共实例字段

Pascal 

RedValue

注意   很少使用。属性优于使用公共实例字段。

区分大小写

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

  • 不要使用要求区分大小写的名称。对于区分大小写和不区分大小写的语言,组件都必须完全可以使用。不区分大小写的语言无法区分同一上下文中仅大小写不同的两个名称。因此,在创建的组件或类中必须避免这种情况。
  • 不要创建仅是名称大小写有区别的两个命名空间。
    • 例如不区分大小写的语言无法区分以下两个命名空间声明
      • namespace ee.cummings;
        
      • namespace Ee.Cummings;
        
  • 不要创建具有仅是大小写有区别的参数名称的函数。下面的示例是不正确的。
    • void MyFunction(string a, string A)
      
  • 不要创建具有仅是大小写有区别的类型名称的命名空间。在下面的示例中,Point pPOINT p 是不适当的类型名称,原因是它们仅在大小写方面有区别。
    • 例如:System.Windows.Forms.Point p
      • System.Windows.Forms.POINT p
  • 不要创建具有仅是大小写有区别的属性名称的类型。在下面的示例中,int Color int COLOR 是不适当的属性名称,原因是它们仅在大小写方面有区别。
    • 例如:int Color {get, set}
      
      • int COLOR {get, set}
        
  • 不要创建具有仅是大小写有区别的方法名称的类型。在下面的示例中,calculate Calculate 是不适当的方法名称,原因是它们仅在大小写方面有区别。
    • 例如:void calculate()
      
      • void Calculate()
        

缩写

为了避免混淆和保证跨语言交互操作,区分缩写使用下列规则:

  • 不要将缩写或缩略形式用作标识符名称的组成部分。例如,使用 GetWindow,而不要使用 GetWin
  • 不要使用计算机领域中未被普遍接受的缩写。
  • 在适当的时候,使用众所周知的缩写替换冗长的词组名称。例如,用 UI 作为 User Interface 的缩写;用 OLAP 作为 On-line Analytical Processing 的缩写。
  • 在使用缩写时,对于超过两个字符长度的缩写,使用 Pascal 大小写或 Camel 大小写。例如,使用 HtmlButton 或 htmlButton。但是,应当大写仅有两个字符的缩写,如,System.IO,而不是 System.Io
  • 不要在标识符或参数名称中使用缩写。如果必须使用缩写,对于由多于两个字符所组成的缩写请使用 Camel 大小写,虽然这和单词的标准缩写相冲突。

    措词

避免使用与常用的 .NET Framework 命名空间重复的类名称。例如,不要将以下任何名称用作类名称:System、Collections、Forms 或 UI。

有关 .NET Framework 命名空间的列表,请参见类库。

另外,避免使用与以下关键字冲突的标识符:

AddHandler

AddressOf

Alias

And

Ansi

As

Assembly

Auto

Base

Boolean

ByRef

Byte

ByVal

Call

Case

Catch

CBool

CByte

CChar

CDate

CDec

CDbl

Char

CInt

Class

CLng

CObj

Const

CShort

CSng

CStr

CType

Date

Decimal

Declare

Default

Delegate

Dim

Do

Double

Each

Else

ElseIf

End

Enum

Erase

Error

Event

Exit

ExternalSource

False

Finalize

Finally

Float

For

Friend

Function

Get

GetType

Goto

Handles

If

Implements

Imports

In

Inherits

Integer

Interface

Is

Let

Lib

Like

Long

Loop

Me

Mod

Module

MustInherit

MustOverride

MyBase

MyClass

Namespace

New

Next

Not

Nothing

NotInheritable

NotOverridable

Object

On

Option

Optional

Or

Overloads

Overridable

Overrides

ParamArray

Preserve

Private

Property

Protected

Public

RaiseEvent

ReadOnly

ReDim

Region

REM

RemoveHandler

Resume

Return

Select

Set

Shadows

Shared

Short

Single

Static

Step

Stop

String

Structure

Sub

SyncLock

Then

Throw

To

True

Try

TypeOf

Unicode

Until

volatile

When

While

With

WithEvents

WriteOnly

Xor

eval

extends

instanceof

package

var

    

    

避免类型名称混淆

不同的编程语言使用不同的术语标识基本托管类型。类库设计人员必须避免使用语言特定的术语。请遵循本节中描述的规则以避免类型名称混淆。

使用描述类型的含义的名称,而不是描述类型的名称。如果参数除了其类型之外没有任何语义含义,那么在这种罕见的情况下请使用一般性名称。

例如,支持将各种数据类型写入到流中的类可以有以下方法。

 [C#]

void Write(double value);

void Write(float value);

void Write(long value);

void Write(int value);

void Write(short value);

不要创建语言特定的方法名称,如下面的示例所示。

 [C#]

void Write(double doubleValue);

void Write(float floatValue);

void Write(long longValue);

void Write(int intValue);

void Write(short shortValue);

下表列出基本数据类型名称和它们的通用替换。

C# 类型名称

Visual Basic 类型名称

JScript 类型名称

Visual C++ 类型名称

<Ilasm.exe> 表示形式

通用类型名称

sbyte

SByte

sByte

char

int8

SByte

byte

Byte

byte

unsigned char

unsigned int8

Byte

short

Short

short

short

int16

Int16

ushort

UInt16

ushort

unsigned short

unsigned int16

UInt16

int

Integer

int

int

int32

Int32

uint

UInt32

uint

unsigned int

unsigned int32

UInt32

long

Long

long

__int64

int64

Int64

ulong

UInt64

ulong

unsigned __int64

unsigned int64

UInt64

float

Single

float

float

float32

Single

double

Double

double

double

float64

Double

bool

Boolean

boolean

bool

bool

Boolean

char

Char

char

wchar_t

char

Char

string

String

string

String

string

String

object

Object

object

Object

object

Object

例如,支持将从流读取各种数据类型的类可以有以下方法。

[C#]

double ReadDouble();

float ReadSingle();

long ReadInt64();

int ReadInt32();

short ReadInt16();

上面的示例优先于下面的语言特定的替代方法。

[C#]

double ReadDouble();

float ReadFloat();

long ReadLong();

int ReadInt();

short ReadShort();

 

命名空间命名规范

以下模板举例说明了命名空间的命名规则。

<Company>.<Technology>[.<Feature>][.Design]

因此我们希望看到这样如下命名空间:

Microsoft.Media

Microsoft.Media.Design

PowerSoft.PowerBuilder.Math

  1. 避免两个PUBLISED的NAMESPACES名称一样的可能。使用公司名称或者一个正式分支的名称如用Microsoft.Office来表示Microsoft提供的Office Automation Classes。
  2. 使用一个稳定的被广泛认可的技术名称作为第二层名称
  3. 不要使用组织的结构层次作为命名空间的层次依据
  4. 请在DESIGN-TIME命名空间前加上.Design前缀以表示基础命名空间DESIGN-TIME特性。如System.Windows.Forms.Design 包含了用来设计System.Windows.Forms 应用程序的的设计器和相关设计类。
  5. 使用PASCAL风格,把每个逻辑部件用分隔号分开(如Microsoft.Office.PowerPoint _)如果你的品牌采用了非传统方式的命名,那么就延续的你的品牌的命名方式,就算他和通常的命名空间法则不同。如(NeXT.WebObjects, and ee.cummings)
  6. 在适当的地方使用复数命名。例如采用System.Collections 而非System.Collection。对此例外的是品牌名和缩写。如用System.IO 而非System.IOs。
  7. 不要让命名空间和类都使用同样的名字。如,当有个类叫Debug时就不要使用Debug来作为命名空间的名字。

    命名空间结构不需和配件名称结构并行。如,当你决定使用给配件取名叫MyCompany.MyTechnology.dll 时,这并不需要你得让配件中包含一个叫MyCompany.MyTechnology的命名空间。

    类命名规范

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

  • 使用名词或名词短语命名类。
  • 使用 Pascal 大小写规则。
  • 少用缩写。
  • 不要使用类型前缀,如在类名称上对类使用 C 前缀。例如,使用类名称 FileStream,而不是 CFileStream
  • 不要使用下划线字符 (_)。
  • 偶尔可能会出现已I作为类名开头的情况,而这个类又不是一个接口类。这种情况下只要第二个字母是小写就可以接受。如IdentityStore.
  • 在适当的地方,使用复合单词命名派生的类。派生类名称的第二个部分应当是基类的名称。例如,ApplicationException 对于从名为 Exception 的类派生的类是适当的名称,原因是 ApplicationException 是一种 Exception。请在应用该规则时进行合理的判断。例如,Button 对于从 Control 派生的类是适当的名称。尽管按钮是一种控件,但是将 Control 作为类名称的一部分将使名称不必要地加长。

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

[C#]

public class FileStream

public class Button

public class String

接口命名规范

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

  • 用名词或名词短语,或者描述行为的形容词命名接口。例如,接口名称 IComponent 使用描述性名词。接口名称 ICustomAttributeProvider 使用名词短语。名称 IPersistable 使用形容词。
  • 使用 Pascal 大小写规则。
  • 少用缩写。
  • 接口名称加上字母 I 前缀,以指示该类型为接口。
  • 在定义类/接口对(其中类是接口的标准实现)时使用相似的名称。两个名称的区别应该只是接口名称上有字母 I 前缀。
  • 不要使用下划线字符 (_)。

以下是正确命名的接口的示例。

[C#]

public interface IServiceProvider

public interface IFormatable

以下代码示例阐释如何定义 IComponent 接口及其标准实现 Component 类。

[C#]

public interface IComponent 

{

   // Implementation code goes here.

}

public class Component: IComponent 

{

   // Implementation code goes here.

}

属性命名规范

将后缀 Attribute 添加到自定义属性类。以下是正确命名的属性类的示例。

[C#]

public class ObsoleteAttribute{}

枚举类型命名规范

枚举 (Enum) 值类型从 <Enum 类>继承。以下规则概述枚举的命名规范:

  • 对于 Enum 类型和值名称使用 Pascal 大小写规则。
  • 少用缩写。
  • 不要在 Enum 类型名称上使用 Enum 后缀。
  • 对大多数 Enum 类型使用单数名称,但是对作为位域的 Enum 类型使用复数名称。
  • 总是将 FlagsAttribute 添加到位域 Enum 类型。

    静态字段命名规范

以下规则概述静态字段的命名规范:

  • 使用名词、名词短语或者名词的缩写命名静态字段。
  • 使用 Pascal 大小写规则。
  • 不要在静态字段名称中使用匈牙利命名法的前缀。
  • 建议尽可能使用静态属性而不是公共静态字段。

    参数命名规范

必须仔细遵守这些参数的命名规范,这非常重要,因为提供上下文相关帮助和类浏览功能的可视化设计工具会在设计器中对用户显示方法参数名称。以下规则概述参数的命名规范:

  • 对参数名称使用 Camel 大小写规则。
  • 使用描述性参数名称,参数名称的含义清晰。
  • 使用描述参数的含义的名称,而不要使用描述参数的类型的名称。
  • 不要使用保留的参数。
  • 不要给参数名称加匈牙利命名法的前缀。

以下是正确命名的参数的示例:

[C#]

Type GetType(string typeName)

string Format(string format, object[] args)

方法命名规范

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

  • 使用动词或动词短语命名方法。
  • 使用 Pascal 大小写规则。

以下是正确命名的方法的示例:

RemoveAll()

GetCharArray()

Invoke()

属性命名规范

以下规则概述属性的命名规范:

  • 使用名词或名词短语命名属性。
  • 使用 Pascal 大小写规则。
  • 不要使用匈牙利命名法。
  • 考虑用与属性的基础类型相同的名称创建属性。例如,如果声明名为 Color 的属性,则属性的类型同样应该是 <Color>

以下代码示例阐释正确的属性命名:

[C#]

public class SampleClass

{

   public Color BackColor 

   {

      // Code for Get and Set accessors goes here.

   }

}

以下代码示例阐释提供其名称与类型相同的属性:

[C#]

public enum Color 

{

   // Insert code for Enum here.

}

public class Control 

{

   public Color Color 

   { 

      get {// Insert code here.} 

      set {// Insert code here.} 

   }

}

以下代码示例不正确,原因是 Color 属性是 Integer 类型的:

[C#]

public enum Color {// Insert code for Enum here.}

public class Control 

{

   public int Color 

   { 

      get {// Insert code here.} 

      set {// Insert code here.}  

   }

}

在不正确的示例中,不可能引用 Color 枚举的成员。Color.Xxx 将被解释为访问一个成员,该成员首先获取 Color 属性(在 Visual Basic 中为 Integer 类型,在 C# 中为 int 类型)的值,然后再访问该值的某个成员(该成员必须是 System.Int32 的实例成员)。

变量命名

变量分为全局变量和局部变量,在变量命名是要加以区分。

全局变量命名应在变量名前加"_"标志。

局部变量则不需要添加标志。

事件命名规范

以下规则概述事件的命名规范:

  • 使用 Pascal 大小写。
  • 不要使用匈牙利命名法。
  • 对事件处理程序名称使用 EventHandler 后缀。
  • 指定两个名为 sender e 的参数。sender 参数表示引发事件的对象。sender 参数始终是 object 类型的,即使在可以使用更为特定的类型时也如此。与事件相关联的状态封装在名为 e 的事件类的实例中。对 e 参数类型使用适当而特定的事件类。
  • EventArgs 后缀命名事件参数类。
  • 考虑用动词命名事件。例如,命名正确的事件名称包括 Clicked、Painting 和 DroppedDown。
  • 使用动名词(动词的"ing"形式)创建表示事件前的概念的事件名称,用过去式表示事件后。例如,可以取消的 Close 事件应当具有 Closing 事件和 Closed 事件。不要使用 BeforeXxx/AfterXxx 命名模式。
  • 不要在类型的事件声明上使用前缀或者后缀。例如,使用 Close,而不要使用 OnClose
  • 通常情况下,对于可以在派生类中重写的事件,应在类型上提供一个受保护的方法(称为 OnXxx)。此方法只应具有事件参数 e,因为发送方总是类型的实例。

以下示例阐释具有适当名称和参数的事件处理程序:

[C#]

public delegate void MouseEventHandler(object sender, MouseEventArgs e);

以下示例阐释正确命名的事件参数类:

[C#]

public class MouseEventArgs : EventArgs 

{

   int x;

   int y;

   public MouseEventArgs(int x, int y) 

      { this.x = x; this.y = y; }

   public int X { get { return x; } } 

   public int Y { get { return y; } } 

}

代码书写规范

排版规范

  • 程序块要采用缩进风格编写,在某些情况下,代码需要有适当的缩进,缩进的空格数为4个,尽可能不使用Tab键(设置一个Tab=4个空格)。

说明:对于由开发工具自动生成的代码可以有不一致。

 

  • 相对独立的程序块之间、变量说明之后必须加空行。前面加了注释以后也要有空格。

示例:如下例子不符合规范。

if (isValid)

{

... // program code

}

Test t = new Test();

 

应如下书写

if (isValid)

{

... // program code

}

 

Test t = new Test();

 

  • 较长的语句(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。

示例:

int test = aaaaaaaaaaaaaa

+ bbbbbbbbbbbbbbb;

 

  • 循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分,长表达式要在低优先级操作符处划分新行,操作符放在新行之首。

示例:

if ((test < MAX_TEST_NUMBER)

&& (IsValidMethod(testBool)))

{

... // program code

}

 

for (i = 0, j = 0; (i < MAX_TEST_NUMBER)

&& (j < test.length); i++, j++)

{

... // program code

}

 

  • 若方法的参数较长,则要进行适当的划分。

示例:

public void TestMethod (string a,string b,string c,

string d,string e);

 

  • 不允许把多个短语句写在一行中,即一行只写一条语句。

示例:如下例子不符合规范。

rect.length = 0; rect.width = 0;

 

应如下书写

rect.length = 0;

rect.width = 0;

 

  • if、for、do、while、case、switch、default等语句自占一行,且if、for、do、while等语句的执行语句部分无论多少都要加括号{}。

示例:如下例子不符合规范。

if (user == NULL) return;

 

应如下书写:

if (user == NULL)

{

return;

}

 

  • 对齐只使用空格键,不使用TAB键。

说明:以免用不同的编辑器阅读程序时,因TAB键所设置的空格数目不同而造成程序布局不整齐。

 

  • 方法的开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格,case语句下的情况处理语句也要遵从语句缩进要求。

示例:

public void TestMethod()

{

        ...// program code

}

 

  • 程序块的分界符(如大括号'{'和'}')应各独占一行并且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构的定义、枚举的定义以及if、for、do、while、switch、case语句中的程序都要采用如上的缩进方式。

示例:如下例子不符合规范。

for (...) {

... // program code

}

 

if (...)

{

... // program code

}

 

void TestMethod()

{

... // program code

}

 

应如下书写。

for (...)

{

... // program code

}

 

if (...)

{

... // program code

}

 

void TestMethod()

{

... // program code

}

 

  • 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格。

说明:采用这种松散方式编写代码的目的是使代码更加清晰。

由于留空格所产生的清晰性是相对的,所以,在已经非常清晰的语句中没有必要再留空格,如果语句已足够清晰则括号内侧(即左括号后面和右括号前面)不需要加空格,多重括号间不必加空格,因为括号已经是最清晰的标志了。

在长语句中,如果需要加的空格非常多,那么应该保持整体清晰,而在局部不加空格。给操作符留空格时不要连续留两个以上空格。

 

示例:

(1) 逗号、分号只在后面加空格。

int a, b, c;

 

(2)比较操作符, 赋值操作符"="、 "+=",算术操作符"+"、"%",逻辑操作符"&&"、"&",位操作符"<<"、"^"等双目操作符的前后加空格。

if (i >= VALUE)

a = b + c;

a += 2;

a = b ^ 2;

 

(3)"!"、"~"、"++"、"--"等单目操作符前后不加空格。

flag = !isEmpty; // 非操作"!"与内容之间

i++; // "++","--"与内容之间

 

(4) if、for、while、switch等与后面的括号间应加空格,使if等关键字更为突出、明显。

if (a >= b && c > d)

 

  • 一行程序以小于80字符为宜,不要写得过长。
  • 成员比较多、代码量较大的类中,为了使代码更容易阅读,应适当使用#region…#end region将同类型的代码加以分隔显示或隐藏。

示例:

#region 属性

... // program code

#endregion

 

#region 私有方法

... // program code

#endregion

类成员使用规范

属性使用指南

使用Pascal风格来命名属性

public class Foo {

public Color BackColor { }

}

 

不要使用匈牙利命名法.

建议不要使用相同的类的属性名字和类型名字.

将一个类的属性名定义成与某个类型(Type)的名字相同会在某些编程语言中引起二义性. 最好避免这种二义性除非由非常正当的理由去这么做。

例如: System.Windows.Forms 类有一个 Icon 属性, 虽然也有一个 Icon 但由于 Form.Icon 这个名字比 Form.FormIcon Form.DisplayIcon 这样的类属性名更容易理解,所以我们仍采用Icon 属性名. 但是在 System.WinForms. UI.Control 类中有一个颜色属性. 由于已经有了一个Color , 这个类的颜色属性就被命名为 BackgroundColor. 这是一个更有意义的命名且与已经存在的类的名字不冲突.

当对一个类中属性作赋值操作时发生了异常(Exception)时,一定要保证这个属性中还保留着以前保存的属性值。避免这个属性的内容处于不确定状态.

必须允许类的属性可以以任意的顺序赋值. 属性与其他属性间应该是没有状态关系的.

通常情况下只有当程序对一组属性作了赋值之后,这个对象的某个功能才会被激活,或者只有当这个对象在一些正确的状态下, 这个功能才会起作用. 而且当对象进入此状态时, 这个功能会被自动激活而无需任何其他的显示(Implicit)调用.

例如一个TextBox 有两个相关的属性: DataSource DataField. DataSource 指明了数据表名, DataField 指明了相应的类属性名. 一旦这两个属性被指明后, 这个控件就会把数据表中相应的数据取出赋给这个控件的Text 属性. 下面的例子演示了我们可以以任意的顺序设置控件的属性.

TextBox t = new TextBox();

t.DataSource = "Publishers";

t.DataField = "AuthorID";

// The data binding feature is now active.

 

上面的代码是与下面的程序等效的.

TextBox t = new TextBox();

t.DataField = "AuthorID";

t.DataSource = "Publishers";

// The data binding feature is now active.

 

此外, 程序员可以将一个属性设为null, 指明这个属性是未赋值的.

TextBox t = new TextBox();

t.DataField = "AuthorID";

t.DataSource = "Publishers";

// The data binding feature now active

 

t.DataSource = null;

// The data binding feature is now inactive

 

下面的例子演示了如何做到对属性的赋值顺序复关,并检查数据绑定的状态并在适当的时候自动激活相应的功能.

public class TextBox {

string dataSource;

string dataField;

boolean active;

 

public string DataSource {

        get {

return dataSource;

}

set {

if (value != dataSource) {

// Update active state.

SetActive(value!= null && dataField != null);

dataSource = value;

}

}

}

 

public string DataField {

get {

return dataField;

}

set {

if (value != dataField) {

// Update active state.

SetActive(dataSource != null && dataField != null);

dataField = value;

 

}

}

}

 

void SetActive(boolean value) {

if (value != active) {

if (value) {

Activate();

Text = dataBase.Value(dataField);

}

else {

Deactivate();

Text = "";

}

active = value; // set active only if success

}

}

 

void Activate() {

// open database

}

 

void Deactivate() {

// close database

}

}

 

只读和只写的属性

当在逻辑上要求用户不能改变类的属性时,需要使用只读属性.

禁止使用只写属性.

 

事件使用指南

使用Pascal风格来命名事件.

不要使用匈牙利命名法.

应该使用"raise"("产生")术语来表示产生事件而不是使用"fire" "trigger".

当在文档中描述事件时, 应该使用"an event was raised" 而不是 "an event was fired" "an event was triggered."

应考虑用动词来命名一个事件.

事件处理程序应返回void. 例如:

public delegate void MouseEventHandler(object sender, MouseEventArgs e);

因该从System.EventArgs 类中派生出事件处理数据类。例如:

public class MouseEventArgs : EventArgs {

}

应该假设在事件处理过程中会发生任何事情(要注意异常处理).

类应该准备事件处理过程会修改任何的事物, 而且需要保证在事件被生成时所有的对象都处于一个合法的状态. 应该考虑使用try/finally 程序块在事件产生的地方. 由于程序员调用了call back函数在这个对象上执行操作, 就不应该在程序控制返回后对程序状态做任何假设. 例如:

public class Button {

ButtonClickHandler onClickHandler;

 

protected void DoClick() {

PaintDown(); // paint button in depressed state

 

try {

OnClick(); // call event handler

}

finally {

// window may be deleted in event handler

if (windowHandle != null) {

PaintUp(); // paint button in normal state

            }

}

}

 

protected virtual void OnClick(ClickEvent e) {

if (onClickHandler != null)

onClickHandler(this, e);

}

}

 

方法使用指南

使用Pascal风格来命名事件.

不要使用匈牙利命名法.

缺省使用非虚函数的方法.

方法重载指南

当一个类中有两个同名的方法可以使用,只不过他们有不同的调用参数时,我们需要方法重载.

当你要在类中提供两个不同的方法而他们的语意相同时,应该使用方法重载.

用重载来实现方法的缺省参数。(缺省参数这种方法无法很好地翻译,因此在通用语言规范CLS中是不允许的).

int String.IndexOf (String name);

int String.IndexOf (String name, int startIndex);

 

当参数个数是变化的时候应该使用方法重载.

当一个方法接受的参数个数必须是可变的时候,我们可以通过重载定义N个参数个数渐长的方法来解决, 同时还提供一个重载的方法跟随一个数组参数(为参数个数大于N的客户). N=3 or N=4 是一个比较适合的方法. 例如:

public class Foo{

public void Bar(string a){

Bar(new string[] {a});

}

 

public void Bar(string a, string b){

Bar(new string[] {a, b});

}

 

public void Bar(string a, string b, string c){

Bar(new string[] {a, b, c});

}

 

public virtual void Bar(string[] args){

// core implementation here

}

}

 

可变长度参数

你可以希望提供一个方法可以接受不定个数的参数 (比如像C语言的 printf 函数). 对于受管理的代码, 我们可以用 ParamsAttribute 模式来构造这种结构. 例如, 你可以用下面的代码来代替多个重载的方法.

void Format (string formatString, params object [] args)

 

你不能使用 VarArgs 式的调用规范 (…) 来实现不定个数的参数,这是应为通用语言规范(CLS)不支持它.

构建函数使用指南

只有当一个类中仅有静态的方法和属性时,我们才能使用私有的构造方法.

public sealed class Enviroment
{
private Enviroment(); // Prevents the class from being created.
//…
}

 

在构造函数中尽量做最少的工作.多余的功能应该留到后续的代码中,当客户代码调用这个功能时在座相应的工作。

 

建议不要在"struct" (value types)中定义一个空的构造函数.

如果一个结构没有构造函数, CLR运行时就会自动将结构的所有成员属性设为0. 这能够提高数组或结构初始化的速度.

 

类的成员变量使用指南

不要将类成员变量设为外界可直接访问的.应考虑提供 get set 的成员存取方法来代替将成员变量设为public.

public struct Point{

private int x;

private int y;

 

public Point(int x, int y){

this.x = x;

this.y = y;

}

 

public int X{

get{

return x;

}

set{

x = value;

}

}

 

public int Y{

get{

return y;

}

set{

y = value;

}

}

}

 

如要将一个成员变量提供给继承的子类,应该使用一个a protected (family) 的方法返回这个成员的值.

public struct Control : Component{

private int handle;

 

protected int Handle{

get{

return handle;

}

}

}

 

通过避免将成员变量直接暴露给开发者,类可以更容易地被改写:

  • 一个成员变量能可以被改成一个属性而同时又能维持二进制兼容.
  • 对类成员的访问方法 get set 能允许日后对类的改进, 比如根据对类属性的调用按需创建对象, 或是添加属性修改的事件处理.

    避免在类成员的名字中使用缩写.

    class Foo

    {

    string url;

    string destinationUrl;

    }

    不要使用匈牙利命名法. 好的名字应该描述语法而不是类型.

    参数名称指南

    一定要对参数作校验. 在每个公共的方法或属性的set accessor内应该对输入参数的合法性作检验。若发现有误应该抛出有意义的异常给程序员. 在这里应该使用System.ArgumentException 异常或它的子类.

    class Foo{

    public int Count{

    get{

    return count;

    }

    set{

    if (count < 0 || count >= MaxValue)

throw new ArgumentOutOfRangeException(

                            Sys.GetString(

                                    "InvalidArgument",

                                    "value",

                                    count.ToString()

                        ));

}

}

 

public void Select(int start, int end){

if (start < 0)

throw new ArgumentException(

                    Sys.GetString(

                        "InvalidArgument",

                        "start",

                    start.ToString()

                ));

if (end < 0)

throw new ArgumentException(

                    Sys.GetString(

                        "InvalidArgument",

                        "end",

                    end.ToString()

         ));

}

}

类型使用指南

类型是在运行时封装的单元. 有三种基本的类型.

类是最基本的一种通用类型。类可以是抽象的或密封的。抽象类需要子类来实现相应的方法. 密封的类不允许被继承.

值类型

值类型代表一系列存储在堆栈中的二进制值.

接口类型

一个接口类型是对值的部分描述,它可以被许多对象类型所支持.

类使用指南

尽量使用类代替其他类型. 在别的类型系统中(比如COM就过分依赖于接口)。在被管理的运行环境中, 类应该是最普遍使用的类型.

基类使用指南

基类是将一组有共同功能的对象组织起来的非常有用的方法。基类可以提供一组缺省的功能, 而同时又允许扩展和定制.

只有当你有一个非常明确的用户场景时,才应该在设计中加入对象的扩展和多态等功能.

例如, 给数据适配器提供一个Interface (IDataAdapter)是非常困难且没有真实意义的: 你依然没有一个程序是基于这些特定接口的, 提供一个接口仅有一些边缘性的好处. 然而, 我们必须保证所有数据适配器的一致性. 虽然一个接口或抽象类在这里并不一定恰当,但一个模式非常重要.

建议你使用基类.

从一个多版本的角度, 接口没有类灵活. 基于一个类, 你可以发布它的1.0版然后在版本2.0 中加入新的方法. 只要这个新的方法不是抽象的, 所有已有的从这个基类继承的子类都能够继续工作而无需改变.

由于接口不支持实现继承, 上面适用于类的模式无法作用在接口上. 在接口中加入新方法等于是在基类中加入新的抽象方法: 由于所有实现这个接口的类都没有实现此新方法,他们都被破坏了.

接口适用于下列条件下:

  • 多个不相关的类必须支持同一个协议.
  • 这些类已经有了基类 (for example, some are UI controls, some are Web services),而c#中是不支持多重继承的.这个时候我们需要使用接口。
  • 实际情况不适合使用聚合.

对于其他的情况, 类的继承是一个更好的模式. 比如, IByteStream 定义为一个接口那么一个类就能实现多个流类型. 而将 ValueEditor 定义成一个抽象的基类是因为从 ValueEditor 继承的子类除了edit values外没有其他的功能.

 

 

值类型使用指南

结构使用指南

在如下的情况中,建议使用struct 类型来满足要求.

  • 只作为简单的类型.
  • 类型实例的大小要在16个字节以下.
  • 类型是不可变的.
  • 值类型是更符合要求的.

public struct Int32: IComparable, IFormattable

{

 

public readonly const int MinValue = -2147483648;

public readonly const int MaxValue = 2147483647;

 

public static string ToString(int i);

public override string ToString();

public static int Parse(string s);

public override int GetHashCode();

public override bool Equals(object obj);

public virtual int CompareTo(object object);

}

 

不要在结构类型中提供一个缺省构造函数. 运行时环境会自动加入一个构造函数将所有的值初始化为0. 这在实例化结构数组时能够大大提高效率.

.

枚举使用指南

应该使用枚举类型作为强类型的参数,属性和返回值. 这样程序会更有意义且很难出错。

同时在参数或属性中使用枚举类型(enum) 可以让开发工具知道这个属性或参数所有的可能值,从而自动提示给程序员。这样可以提高开发的效率.

public enum SpecialFolder{

ApplicationData,

UserData ,

Cookies,

DesktopDirectory,

Favorites,

History,

InternetCache,

Programs,

Recent,

Sendto,

StartMenu,

Startup,

Templates
}

 

Type.CreateInstance (IBinder binder, BindingEnum bindAttributes, Variant [] args)

 

如果二进制的OR操作符会作用在枚举类型里的数值上时,一定要在此枚举类型上加上 System.Flags 属性.

[Flags]
public enum Bindings{

IgnoreCase = 0x01;

    NonPublic = 0x02;

    Static = 0x04;

    InvokeMethod = 0x0100;

    CreateInstance    = 0x0200;

    GetField = 0x0400;

    SetField = 0x0800;

    GetProperty    = 0x1000;

    SetProperty    = 0x2000;

    DefaultBinding    = 0x010000;

    DefaultChangeType = 0x020000;

    Default = DefaultBinding | DefaultChangeType;

    ExactBinding = 0x040000;

    ExactChangeType = 0x080000;

    BinderBinding = 0x100000;

    BinderChangeType = 0x200000;

}

 

不要假设枚举类型(enum)的参数会在定义的范围之内,一定要做参数校验.

public void SetColor (Color color)
{
if (!Enum.IsDefined (typeof(color), color)

throw new ArgumentOutOfRangeException();
}

 

如果你用了多个相关的常量(static final constants),那应该考虑用enum 类型代替他们.

 

程序代理使用指南

程序代理是一个强有力的工具,它允许我们在可管理的对象模型中包装方法调用. 它主要是用在以下两种情况中.

事件提醒

作为事件处理函数,在事件触发提供回调机制.4.9节事件命名规范。

回调函数

在用户代码中提供一个回调函数以使得用户代码在执行过程中随时能调用这个函数来做一些定制化的工作. 典型的例子是在一个排序的过程中提供一个比较的回调函数实现通用性。

针对事件处理一定要采用事件设计的模式(即使这不是用户界面相关的).

程序属性(Attribute)的使用

.NET Frameworks允许程序员可以自己发明一些新的定义方法对程序中的一些实体定义特定的信息, 同时允许在运行时从程序中提取这些信息. 比如, 一个新的框架也许要定义一个HelpAttribute的属性从而能放在程序的元素中(如类和类的方法)来提供从这些程序元素到他们的文档的映射. 新的定义信息就是通过定义属性类(attribute classes)来实现.

在定义定制的属性类时一定要在类名后加上Attribute后缀。

public class ObsoleteAttribute{}

 

在属性类的定义中必须加上AttributeUsage 的定义来说明你的程序属性(attributes)的使用方法.

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]

public class ObsoleteAttribute{}

 

异常的产生和处理

使用Exception作为异常类的后缀.

public class FileNotFoundException : IOException {

}

 

推荐使用以下通用的构造函数

public class XxxException : YyyException {

XxxException() {}

XxxException(string message) {}

XxxException(string message, Exception inner) {}

    XxxException(SerializationInfo info, StreamingContext context) {}

}

 

推荐使用预定义的异常类型.只有为场景定义了新的例外类型才能让使用你程序库的用户能根据例外的类型做恰当的处理动作.(而非使用例外的字符串来进行解析,这从性能和维护的角度看是非常糟糕的)

引入一个新的异常类可以让开发人员按照你的新的例外类型采取不同的动作.除非对开发人员有一个场景必须需要异常类,不要去定义一个新的异常类.

举例而言,定义一个FileNotFoundException 是有意义的,因为开发人员可能需要决定是否要创建一个丢失的文件.而对FileIOException就不能明确地用代码作出处理..

不要直接从Exception中派生一个新类.

System* 命名空间中,可以直接继承SystemException.在其他命名空间中,ApplicationException中继承.

根据不同的命名空间来判断该使用ApplicationException或者SystemException来派生新的异常类.,所有的System.IO的异常是从IOException (SystemException派生) 派生下来.而所有Microsoft.Media的异常可以从MediaException派生下来(ApplicationException派生).

要本地化异常消息(用中文描述异常消息).

应该在程序的异常信息后加上结束的标点符号.在一个异常中的描述的句子应该用句号分开.这样显示给用户的异常信息才会清晰。

 

当只有特定情况下需要例外的额外信息时,才需要设置额外的属性(除了提供消息外).你很少需要在例外中包含其他信息.D

只在例外情况下才甩出异常

  • 不要为正常或预期之中的错误使用异常Do not use exceptions for normal or expected errors.
  • 不要在正常的控制流程中使用异常Do not use exceptions for normal flow of control.

推荐返回null来作为通常的错误返回.如文件没有找到,那么File.Open就返回一个null,但如果文件被锁住了就应该激发一个异常.

类在设计时在正常流程中不应抛出异常.在以下例子中,文件流类使用了另外一个办法来避免当文件访问到尾部时触发异常.

class Foo

{

void Bar()

{

FileStream stream = File.Open("myfile.txt");

byte b;

 

// ReadByte returns -1 at EOF

while ((b = stream.ReadByte()) > = 0)

{

// do something

}

}

}

 

如果在不适当的状态下,就应激发一个InvalidOperationException .Do throw an InvalidOperationException if in an inappropriate state.

如果因为在当前对象的状态下, 不恰当的设置属性或调用方法应该激发一个System.InvalidOperationException .

如果有错误的参数被传递或被检测到,就甩出一个ArgumentException.

应能在堆栈中分别出激发例外的那个地方.而不是它被用new 操作符创建的点,你应该仔细考虑在哪里激发异常.

使用异常BUILDER方法..

通常在类中不同的执行的地方都会甩出同样的异常.为避免代码膨胀, HELPER方法来new一个异常再返回这个异常.举例而言:

class File{

string fileName;

 

public byte[] Read(int bytes){

if (!ReadFile(handle, bytes))

throw NewFileIOException();

}

 

FileException NewFileException(){

string description = // build localized string, including fileName

return new FileException(description);

}

}

 

 

宁愿用异常而不要使用错误代码Do throw Exceptions rather than return an error code.

理由有三点:1、使用异常使的代码更容易被理解,如果使用错误代码,编码人员或维护人员将不得不再去查某个文档来获知错误代码的含义;2、异常可提供更多的错误信息,从异常名称上可以大致了解错误的类型,更可以从异常的错误信息及程序堆栈信息上了解到更详细的内容,便于追踪调试;3、异常是强迫性的,如果异常未被捕获并被处理,程序将中止执行,而错误代码则不是,如果在程序中为对错误代码做处理,程序就忽略这个错误,而继续执行下去,这可能会对程序的执行结果产生非常严重的不良后果。

激发异常应尽量具体Do throw the most specific exception possible.

请尽量使用存在的异常而不是去创建一个新的异常Do favor using existing exceptions over creating new ones.

设置所有你使用的异常的成员

使用 Inner exceptions (chained exceptions).

请使用对开发人员有意义的异常信息.Do set meaningful message text targeted at the developer in the exception.

请不要提供方法来激发NullReferenceException IndexOutOfRangeException.

protected(family)和内部(assemly)成员进行参数检查.如果没有对保护的方法进行参数检查,应在文档中进行清晰的声明。除非有其他的声明,应确定在代码中进行参数检查。如果不做参数检查,可能对性能有提高。

应该在抛出异常时清除所有的相关影响.调用者应该能断言当一个函数抛出异常时没有其他 的影响.

: Hashtable.Insert 抛出一个异常,那么调用者应能断言在Hashtable中没有加入一个节点.

标准异常类型

下表分解了运行时的标准错误,并描述了你应该在哪种情况下使用哪个来派生类.

异常类型

基类

描述

举例

Exception 

Object 

Base class for all Exceptions.  

None (use a derived class of this exception). 

SystemException

Exception 

Base class for all runtime generated errors. 

None (use a derived class of this exception). 

IndexOutOfRangeException

SystemException

Thrown only by the runtime when an array is indexed improperly.  

Indexing an array outside of its valid range:

arr[arr.Length+1] 

NullReferenceException 

SystemException 

Thrown only by the runtime when a null object is referenced.  

object o = null;

o.ToString();

InvalidOperationException 

SystemException 

Thrown by methods when in an invalid state. 

Calling Enumerator.GetNext() after removing an Item from the underlying collection. 

ArgumentException 

SystemException 

Base class for all Argument Exceptions. Derived classes of this exception should be thrown where applicable.

None (use a derived class of this exception). 

ArgumentNullException 

ArgumentException 

Thrown by methods that do not allow an argument to be null. 

String s = null;

"foo".IndexOf (s); 

ArgumentOutOfRangeException

ArgumentException 

Thrown by methods that verify that arguments are in a given range. 

String s = "string";

s.Chars[9]; 

InteropException 

SystemException 

Base class for exceptions that occur or are targeted at environments outside of the runtime.

None (use a derived class of this exception). 

ComException 

InteropException 

Exception encapsulating COM Hresult information. 

Used in COM Interop. 

SEHException 

InteropException 

Exception encapsulating Win32 structured Exception Handling information.

Used in unmanaged code Interop. 

异常的包装Wrapping Exceptions

组件中底层的错误应该被包装成对最终用户有意义的异常.

  • Get a "real" example here

public class TextReader

{

public String ReadLine ()

{

try

{

//read a line from the stream

}

catch (Exception e)

{

throw new IOException ("Could not read from stream", e);

}

}}

数组使用指南

数组vs. 集合

类库的设计者有时会面临是使用数组或是集合的选择。这两个方案都有相似的应用模式,蛋在性能特性上有些不同。

集合

当需要有Add, Remove 或需要集合支持的方法时,就采用集合。这个包括所有与集合相关的方法。

使用集合来对内部数组进行只读包装

集合中可索引的属性

使用可索引的属性作为一个集合类或接口类的默认成员Use indexed properties only as the default member of a collection class or interface.

不要在非集合类型中使用下面的这些方法实现。有ADDITEMCOUNT就意味着应该使用集合类型。.

int FooCount { get; }

SomeType Foo[int index] { set; get; }

void ApppendFoo(SomeType foo)

 

数组值属性

推荐使用集合来避免以下代码的低效。

for (int i = 0; i < obj.myObj.Count; i++)

DoSomething(obj.myObj[i])

返回空数组

字符串和数组类型的属性绝不应该返回null.这个原因是因为用户不会理解null.他们总是会相信如下的代码会正确运行

public void DoSomething(…){

string s = SomeOtherFunc();

if (s.Length > 0){

// do something else

}

}

 

通常的规则是null,空字符串和空(无元素)数组应该同样处理

应该返回一个空数组而非一个空引用。

    前台控件命名方法

控件名简写+英文描述,英文描述首字母大写

主要控件名简写对照表

控件名

简写

控件名

简写

Label

lbl

TextBox

txt

Button

btn

LinkButton

lnkbtn

ImageButton

imgbtn

DropDownList

ddl

ListBox

lst

DataGrid

dg

DataList

dl

CheckBox

chk

CheckBoxList

chkls

RadioButton

rdo

RadioButtonList

rdolt

Image

img

Panel

pnl

Calender

cld

AdRotator

ar

Table

tbl

RequiredFieldValidator

rv

RegularExpressionValidator

rev

ValidatorSummary

vs

CrystalReportViewer

rptvew

数据库设计开发规范

命名规范

命名要素

命名要求

示例

备注

表名

名词

Order 

如果有系统表与自定义表可以加前缀

视图

vw+_+表名

Vw_Order

 

过程

sp+_+动词+_+名词

sp_Input_Order

参数名采用第一个字母小写的Camel

函数

fn+_+动词+名词

fn_InputOrder

参数名采用第一个字母小写的Camel

索引

ix+_+表名+_字段名

ix_Order_ID

 

触发器

tg+_+表名+触发事件类型

tg_Order_Add

 
       
       

表结构设计遵守3NF 标准3NF 规定:

A.表内的每一个值都只能被表达一次。

B.表内的每一行都应该被唯一的标识(有唯一键)。

C.表内不应该存储依赖于其他键的非键信息。

字段设计要求

  1. 日期类型使用Datetime,
  2. 与数字相关的字段类型统一用numeric(M,N)格式。
  3. 整数类型用int.
  4. 保存双字节字符须使用nvarchar类型.
  5. 逻辑或标识的字段不用bit而用char(1)来表示方便编程和提高运算速度.
  6. 图象字段采用Image类型.
  7. 主键一般要无意义,没有特殊的需要不要用复合主键(如果是由几个表派生出来的关系数据表可以使用复合主键),可以采用sql server默认的自增加的ID字段做主键。
  8. Not Null字段要加默认值。

    视图使用原则

  9. 视图字段名要尽量使用源表字段名,如果有重复,加上源表名作前缀。
  10. 增删改等操作不要直接使用视图,要使用源表进行。
  11. 如果系统对数据结构不需要保密,则只有对于复杂的、经常使用查询结果,才有必要使用视图。
  12. 如果系统要求数据结构保密,可以大量使用视图。

存储过程建立规则

  1. 存储过程要有异常处理部分,确保过程的可执行性。
  2. 尽量在脚本加入设计思路的注释。
  3. 尽量减少临时表的使用,如确实需要,可以考虑使用表变量替代。
  4. 参数的命名应与字段命名一致,例如:@intEmployeeId
  5. 条件语句块(statenemt block,以 begin…end为边界)仅在if子句的条件为真时才被执行。为提高代码的可读性,建议嵌套不多于5层。还有,当嵌套层次太多时,应该考虑是否可以使用case语句。

    函数建立规则

  6. 函数要有异常处理部分,确保过程的可执行性。
  7. 尽量在脚本加入设计思路的注释。

     

编写函数文本--如触发器、存储过程以及其他数据对象--时,必须为每个函数增加适当注释。该注释以多行注释为主,主要结构如下:

/*******************************************************************************************

* 存储过程:读取用户所分配的操作模块及具体权限

* 编 制 者 :saiko

* 修 改 人 :

* 编制日期:2004.11.27

* 输入参数:共有2个参数

* @cUserNo:用户编号

* @cUserPassword:验证密码

* 输出参数:@eResult 1. 成功= 销售@0001 2. 失败 =Null 3.与 @eResult 无关 输出是记录集

* 调用例子:declare @eResult varchar(50)exec uChkUserPerm '0755' ,'123456', '销售'

* 说 明:

*

******************************************************************************************/

CREATE PROCEDURE spoxxx

 

触发器使用要求

  1. 尽量少使用触发器。如果有必要,则要综合考虑触发器的效率建立。

有外键关联的表,尽量使用外键的update、delete属性替代触发器的功能。

注释

注释可以包含在批处理中。在触发器、存储过程中包含描述性注释将大大增加文本的可读性和可维护性。本规范建议:

1、注释以英文为主。

实际应用中,发现以中文注释的SQL语句版本在英文环境中不可用。为避免后续版本执行过程中发生某些异常错误,建议使用英文注释。

2、注释尽可能详细、全面。

创建每一数据对象前,应具体描述该对象的功能和用途。

传入参数的含义应该有所说明。如果取值范围确定,也应该一并说明。取值有特定含义的变量(如boolean类型变量),应给出每个值的含义。

3、注释语法包含两种情况:单行注释、多行注释

单行注释:注释前有两个连字符(--),最后以行尾序列(CR-LF)结束。一般,对变量、条件子句可以采用该类注释。

多行注释:符号/*和*/之间的内容为注释内容。对某项完整的操作建议使用该类注释。

4、注释简洁,同时应描述清晰。

附录一: 匈牙利命名法

匈牙利命名法是一名匈牙利程序员发明的,而且他在微软工作了多年。此命名法就是通过微软的各种产品和文档传出来的。多数有经验的程序员,不管他们用的是哪门儿语言,都或多或少在使用它。

这种命名法的基本原则是:

变量名属性类型对象描述

即一个变量名是由三部分信息组成,这样,程序员很容易理解变量的类型、用途,而且便于记忆。

下边是一些推荐使用的规则例子,你可以挑选使用,也可以根据个人喜好作些修改再用之。

⑴属性部分:

全局变量: g_

常量 : c_

类成员变量: m_

⑵类型部分:

指针: p

句柄: h

布尔型: b

浮点型: f

无符号: u

⑶描述部分:

初始化: Init

临时变量: Tmp

目的对象: Dst

源对象: Src

窗口: Wnd

下边举例说明:

hwnd: h表示句柄,wnd表示窗口,合起来为"窗口句柄"。

m_bFlag: m表示成员变量,b表示布尔,合起来为:"某个类的成员变量,布尔型,是一个状态标志"。

以前,多数程序员喜欢把数据类型作为变量名的前缀而m_作为成员变量的前缀。例如: string m_sName;int nAge;

然而,这种方式在.NET编码规范中是不推荐的。所有变量都用Camel 大小写形式,而不是用数据类型和m_来作前缀。

用name,address,salary等代替nam,addr,sal。

别使用单个字母的变量象i,n,x 等。使用 index,temp等。用于循环迭代的变量例外:

如果变量只用于迭代计数,没有在循环的其他地方出现,允许用单个字母的变量命名,而不是另外取实义名。

posted @ 2011-04-27 08:39  孙同海  阅读(1680)  评论(6编辑  收藏  举报