Skip to content

开发规范(代码)

编码规范

命名规范

编程的命名方式主要有Pascal和Camel两种:

  • Pascal:每个单词的首字母大写,例如ProductType

  • Camel:首个单词的首字母小写,其余单词的首字母大写,例如productType

以下是一些常用的C#成员及其推荐命名方法:

标志符规则实例与描述
类classPascalApplication
枚举类型enumPascalEditStyle
委托delegatePascal以Pascal命名,不以任何特殊字符串区别于类名、函数名
常量const全部大写全部大写,单词间以下划线隔开
接口interfacePascalIDisposable 注:总是以 I 前缀开始,后接Pascal命名
方法functionPascalToString
命名空间namespacePascal以.分隔,当每一个限定词均为Pascal命名方式,比如: using RV.Entity
参数Camel首字母小写
局部变量Camel也可以加入类型标识符,比如对于System.String类型,声明变量是以str开头,string strUserName = string.Empty;
数据成员Camel以_开头+Pascal命名规则,如_ProductType(m意味member)
属性Pascal

标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。

命名中若使用特殊约定或缩写,则要有注释说明。

说明:应该在源文件的开始之处,对文件中所使用的缩写或约定,特别是特殊的缩写,进行必要的注释说明。

自己特有的命名风格,要自始至终保持一致,不可来回变化。

说明:个人的命名风格,在符合所在项目组或产品组的命名规则的前提下,才可使用。(即命名规则中没有规定到的地方才可有个人命名风格)。

对于变量命名,禁止取单个字符(如i、j、k...),建议除了要有具体含义外,还能表明其变量类型、数据类型等,但i、j、k作局部循环变量是允许的。

说明:变量,尤其是局部变量,如果用单个字符表示,很容易敲错(如i写成j),而编译时又检查不出来,有可能为了这个小小的错误而花费大量的查错时间。

命名规范必须与所使用的系统风格保持一致,并在同一项目中统一,比如采用UNIX的全小写加下划线的风格或大小写混排的方式,不要使用大小写与下划线混排的方式,用作特殊标识如标识成员变量的_,其后加上大小写混排的方式是允许的。

示例: Add_User不允许,add_userAddUser_AddUser允许。

除非必要,不要用数字或较奇怪的字符来定义标识符。

示例:如下命名,使人产生疑惑。

csharp
const _EXAMPLE_0_TEST_
const _EXAMPLE_1_TEST_

在同一软件产品内,应规划好接口部分(互相调用的情况)标识符(属性、方法等)的命名,以便于使用。

说明:对接口部分的标识符应该有限制,防止冲突。如可规定接口部分的变量与常量之前加上“模块”标识等。

用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。

说明:下面是一些在软件中常用的反义词组。

add / remove begin / end create / destroy

insert / delete first / last get / release

increment / decrement put / get

add / delete lock / unlock open / close

min / max old / new start / stop

next / previous source / target show / hide

send / receive source / destination

cut / paste up / down

示例:

csharp
int  min_sum;

int  max_sum;

int  add_user();

int  delete_user();

命名规范示例

  • Namespace

命名空间一般性规则是使用公司名称,后跟技术名称和可选的功能与设计。

如下所示:CompanyName.TechnologyName[.Feature][.Design]

例如:

csharp
namespace RV.UI.Frame
  • Normal Class

使用 Pascal 大小写,使用名词或名词短语命名类。

尽量用全称避免缩写,除非缩写已是一种公认的约定,如URL、HTML

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

尽量不要使用下划线字符“ _ ”

正确的类命名不正确的类命名
public class FileStreampublic class CFileStream
public class ReWriteUrlpublic class ReWrite_Url
  • Interface

整体上类似类命名规范,但是接口名称要加上字母 I 前缀,以指示该类型为接口。

举例:

csharp
public interface ISession

public interface IQuery
  • Abstract Class

整体上类似类命名规范,但是抽象类名称要加上字母 Abstract 前缀,以指示该类型为抽象类。

举例:

csharp
public abstract class AbstractServiceProvider
  • Method

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

例如:

csharp
GetUserByUserID()

Initalization()
  • Property

使用 Pascal 大小写,使用名词或名词短语命名属性。

例如:

csharp
public int UserName
{
// TODO
}
  • Parameter

对参数名称使用 Camel 大小写。

使用描述性参数名称。参数名称应当具有足够的描述性,以便参数的名称及其类型可用于在大多数情况下确定它的含义。

例如:

csharp
Type GetUserName(string userID)

void GenerateCode(string tableName, object objParam)
  • Variable

Prefixes for Variable Scope

前缀(Prefix)描述(Description)例子(Example)
_Local to module or form(成员变量)_blnDataChanged
(no prefix)local to procedure(函数中的变量)intIndex

Naming Standard for Variable Data Types/ Database Objects/ ActiveX Controls / Standard Controls

类型前缀举例
BatManagerbarmgr
BindingSourcebscbscTbFactory
BitmapbmpbmpErr
Button/simpleButtonbtnbtnConfirm
ButtonEditbepbepMaterial
ChartControlchart
Checkbox/CheckEditchkchkPrint
Combobox/ComboboxEditcbocboTitle
Common dialogdlgdlgFileOpen
ComponentcptcptHello
ControlctlctlLastControl
Directory list boxdirdirSource
DockManagerdockmgr
DropDownListdpddpdCollieryNo
ErrObjecterrerrLastError
FieldfldfldLastName
Formfrmfrm<sys><scrnID>
GridColumncolcolUserId
GridControlgcgcRole
GridViewgvgvRole
GroupControlgrpgrpListFn
HandlehndHndPicture
Horizontal Scroll barhsbHsbVolume
ImageimgImgIcon
ImageCollection/ImageListimglst
ImageEditimgeimgeUser
Label/LableControllblLblHelpMessage
LayoutControllayout
LinelinLinVertical
List boxlbxLstResultCodes
ListViewlvwLvwFiles
LookUpEdit/GridLookUpEditluelueFactory
MenumnuMnuFileOpen
OLE containeroleOlePhoto
Option buttonoptOptSpanish
OutlineoutOutOrgChart
Panel/PanelControlpnlPnlSettings
Picture boxpicPicDiskSpace
Picture clipclpClpToolbar
PivotGridControlpgc
PopupMenupopmnu
RadioGrouprdprdpOption
ReportrptrptAY_Production
RepositoryItemrep<control>repDtpStart
spinEditspespeNum
Splitter/SplitterControlspcspcQuery
TableLayoutPaneltlptlpQuery
TextBox/TextEdit/MemoEdittxttxtAddress
ToolStriptstsFactory
ToolStripButtonbtnbtnSave
Tooltip/superTooltiptip
TreeView/TreeListtrvtrvFolders
UserControlucucHello
Vertical scroll barvsbvsbRate
VGridControlvgc
xtraTabControltbctbcCustomerInfo
xtraTabPagetbptbpAddress

类成员(全局)变量命名规范

采用Camel大小写的方式,前面加_前缀

例如:

csharp
private int _userId;

private string _userName;

private string _password;

局部变量命名规范(统一加入类型标识符前缀)

例如:

csharp
int rowsCount = 1;

string tableName = “AY_User”;

string userName = "Admin";

控件命名规范

  • Constants

所有单词大写,多个单词之间用 "_" 隔开。

例如:

csharp
public const string CONFIG_FILE = " app.config";
  • 缩写

规则:较短的单词可通过去掉“元音”形成缩写;较长的单词可取单词的头几个字母形成缩写;一些单词有大家公认的缩写.

全写缩写全写缩写全写缩写
averageavgescapeesclibrarylib
backbkflagflgmanagermngr(mgr)
backgroundbgformfrmmessagemsg
breakbrkgridgrdOracleOra
bufferbufincrementincpanoramapano
colorcr(clr)informationinfopasswordpwd
controlctrlinitialinitpicturepic
datadatinsertinspointpt
deletedelimageimgpositionpos
documentdoclabellabprintprn
editedtlengthlenprogramprg
errorerrlistlstserversrv

编码规范

  • 声明

一行只建议作一个声明,如:

csharp
int rowCount;   //推荐

int x, y;    //不推荐

初始化

建议在变量声明时就对其做初始化。

csharp
int rowCount = 0;

bool lnExist = false;

位置

变量建议置于块的开始处,不要总是在第一次使用它们的地方做声明。

csharp
void MyMethod()
{

int int1 = 0;         // beginning of method block


if (condition)
{

int int2 = 0;     // beginning of "if" block

...

}
}
  • 注释

注释应当遵守代码整洁的建议,仅在需要的地方加注释;对外公开的类、接口和方法需要有注释,以方便形成帮助文档,具体注释方式详见下面内容;如果有注释,在修改代码的时候务必要同时修改注释。

  • 文件注释

在代码文件的头部进行注释,这样做的好处在于,我们能对代码文件做变更跟踪。在代码头部分标注出创始人、创始时间、修改人、修改时间、代码的功能,这在团队开发中必不可少,它们可以使后来维护/修改的同伴在遇到问题时,在第一时间知道他应该向谁去寻求帮助,并且知道这个文件经历了多少次迭代、经历了多少个程序员的开发和修改。如:

csharp
/********************************************************************

** Copyright (C), 1998-2010, RVSOFT Tech. Co., Ltd.

* File Name:       PPBaseInfo.cs

* Author:          cli

* Create Time:     2010-09-15 20:09

* Modifier:        zhaxg

* Modify Time:     2010-09-15 23:09

* Description:     用户表实体类

********************************************************************/
  • 类与属性注释
csharp
/// <summary>
/// 生产组公用基础信息
/// </summary>
public class PPBaseInfo{}
csharp
/// <summary>
/// 工厂基础信息表
/// </summary>
public static TbFactoryList FactoryList{get;set;}
  • 函数与方法注释
csharp
/// <summary>
/// 构造函数注释
/// </summary>
/// <param name="a_Param">参数注释</param>
public PPBaseInfo(string a_Param)
{

this._Param = a_Param;

}
csharp
/// <summary>
/// 计算两个整型变量的加法运算结果
/// </summary>
/// <param name="a_Param1">参数1</param>
/// <param name="a_Param2">参数2</param>
/// <returns>运算结果</returns>
public int GetSumResult(int a_Param1, int a_Param2)
{
return a_Param1 + a_Param2;
}

一般情况下,涉及到逻辑运算的,或者说有明确的调用者调用该函数方法的,一定要注明函数注释。事件类的方法可以不注明方法注释。

  • 语句与代码块注释
csharp
/// <summary>
/// 计算两个整型变量的加法运算结果
/// </summary>
/// <param name="a_Param1">参数1</param>
/// <param name="a_Param2">参数2</param>
/// <returns>运算结果</returns>
public int GetSumResult(int a_Param1, int a_Param2)
{
//存储计算结果

int result = 0;


//开始计算

result = a_Param1 + a_Param2;

//返回结果
return result;
}

语句与代码块注释相同,原则上是在语句的上方注释,以//开头,使用中文注释,为了后期维护方便,禁止使用英文注释。注释一般与上一语句隔一行。

  • 排版

程序块要采用缩进风格编写,缩进的空格数为4个。

说明:VisualStudio会自动处理该问题,CTRL+E,D

相对独立的程序块之间、变量说明之后必须加空行。

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

c
if (!valid_user(user))
{

... // program code

}

repssn_ind = ssn_data[index].repssn_index;

repssn_ni  = ssn_data[index].ni;

应如下书写

c
if (!valid_user(user))
{

... // program code

}


repssn_ind = ssn_data[index].repssn_index;

repssn_ni  = ssn_data[index].ni;

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

示例:

c
report_or_not_flag = ((taskno < MAX_ACT_TASK_NUMBER)

&& (n7stat_stat_item_valid (stat_item))

&& (act_task_table[taskno].result_data != 0));

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

c
if ((taskno < max_act_task_number)

&& (n7stat_stat_item_valid (stat_item)))

{

... // program code

}
c
for (i = 0, j = 0; (i < BufferKeyword[word_index].word_length)

&& (j < NewKeyword.word_length); i++, j++)

{

... // program code

}
c
for (i = 0, j = 0;

(i < first_word_length) && (j < second_word_length);

i++, j++)

{

... // program code

}

若函数或过程中的参数较长,则要进行适当的划分。

示例:

csharp
public OracleParameter GetParameter(string name,

OracleType type,

ParameterDirection Direction,

Object Value)

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

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

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

应如下书写

c
rect.length = 0;

rect.width  = 0;

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

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

c
if (pUserCR == NULL) return;

应如下书写:

c
if (pUserCR == NULL)
{

return;

}

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

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

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

c
for (...) {

... // program code

}

if (...)
{

... // program code

}


void example_fun( void )
{

... // program code

}

应如下书写。

c
for (...)
{

... // program code

}

if (...)
{

... // program code

}


void example_fun()
{

... // program code

}

在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非等对操作时,如果是关系密切的立即操作符,后不应加空格。

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

该项由开发工具自动完成,CTRL+E,D

一行程序以小于80字符为宜,不要写得过长。

  • 方法

明确方法功能,精确(而不是近似)地实现方法设计。

在同一项目组应明确规定对接口方法参数的合法性检查应由方法的调用者负责。

说明:对于模块间接口方法的参数的合法性检查这一问题,往往有两个极端现象,即:要么是调用者和被调用者对参数均不作合法性检查,结果就遗漏了合法性检查这一必要的处理过程,造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查,这种情况虽不会造成问题,但产生了冗余代码,降低了效率。

防止将方法的参数作为工作变量。

说明:将方法的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。

示例:下方法的实现不太好。

c
void sum_data(int num, int data, int sum )
{

sum = 0;

for (int count = 0; count < num; count++)
{

sum  += data; // sum成了工作变量,不太好。

}
}

若改为如下,则更好些。

c
void sum_data(int num, int data, int sum )
{

int sum_temp = 0;


for (int count = 0; count < num; count ++)
{

sum_temp  += data;

}

sum = sum_temp;

}

方法的规模限制在50行以内。

说明:不包括注释和空格行。

一个方法仅完成一件功能,一件事。

为简单功能编写方法。

说明:虽然为仅用一两行就可完成的功能去编方法好象没有必要,但用方法可使功能明确化,增加程序可读性,亦可方便维护、测试。

示例:如下语句的功能不很明显。

c
value = ( a > b ) ? a : b ;

改为如下就很清晰了。

c
int max (int a, int b)
{
return ((a > b) ? a : b);
}


value = max (a, b);

不要设计多用途面面俱到的方法。

说明:多功能集于一身的方法,很可能使方法的理解、测试、维护等变得困难。

方法的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出。

尽量不要编写依赖于其他方法内部实现的方法。

说明:此条为方法独立性的基本要求。

避免设计多参数方法,不使用的参数从接口中去掉。

说明:目的减少方法间接口的复杂度。

检查方法所有参数输入的有效性。

检查方法所有非参数输入的有效性,如数据文件、公共变量等。

说明:方法的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。公共方法在使用输入之前,应进行必要的检查。

方法名应准确描述方法的功能。

使用动宾词组为执行某操作的方法命名。如果是OOP方法,可以只有动词(名词是对象本身)。

示例:参照如下方式命名方法。

c
void print_record(int rec_ind ) ;

int  input_record( void ) ;

unsigned char get_current_color( void ) ;

避免使用无意义或含义不清的动词为方法命名。

说明:避免用含义不清的动词如process、handle等为方法命名,因为这些动词并没有说明要具体做什么。

方法的返回值要清楚、明了,让使用者不容易忽视错误情况。

说明:方法的每种出错返回值的意义要清晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。

不要把与方法返回值类型不同的变量,以编译系统默认的转换方式或强制的转换方式作为返回值返回。

让方法在调用点显得易懂、容易理解。

在调用方法填写参数时,应尽量减少没有必要的默认数据类型转换或强制数据类型转换。

说明:因为数据类型转换或多或少存在危险。

避免方法中不必要语句,防止程序中的垃圾代码。

说明:程序中的垃圾代码不仅占用额外的空间,而且还常常影响程序的功能与性能,很可能给程序的测试、维护等造成不必要的麻烦。

防止把没有关联的语句放到一个方法中。

说明:防止方法或过程内出现随机内聚。随机内聚是指将没有关联或关联很弱的语句放到同一个方法或过程中。随机内聚给方法或过程的维护、测试及以后的升级等造成了不便,同时也使方法或过程的功能不明确。使用随机内聚方法,常常容易出现在一种应用场合需要改进此方法,而另一种应用场合又不允许这种改进,从而陷入困境。

在编程时,经常遇到在不同方法中使用相同的代码,许多开发人员都愿把这些代码提出来,并构成一个新方法。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造将产生随机内聚的方法。

示例:如下方法就是一种随机内聚。

c
void Init_Var()
{

Rect.length = 0;

Rect.width = 0; /* 初始化矩形的长与宽 */


Point.x = 10;

Point.y = 10;   /* 初始化“点”的坐标 */

}

矩形的长、宽与点的坐标基本没有任何关系,故以上方法是随机内聚。

应如下分为两个方法:

c
void Init_Rect()
{

Rect.length = 0;

Rect.width = 0; /* 初始化矩形的长与宽 */

}


void Init_Point( void )
{

Point.x = 10;

Point.y = 10;   /* 初始化“点”的坐标 */

}

如果多段代码重复做同一件事情,那么在方法的划分上可能存在问题。

说明:若此段代码各语句之间有实质性关联并且是完成同一件功能的,那么可考虑把此段代码构造成一个新的方法。

减少方法本身或方法间的递归调用。

说明:递归调用特别是方法间的递归调用(如A->B->C->A),影响程序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。

仔细分析模块的功能及性能需求,并进一步细分,同时若有必要画出有关数据流图,据此来进行模块的方法划分与组织。

说明:方法的划分与组织是模块的实现过程中很关键的步骤,如何划分出合理的方法结构,关系到模块的最终效率和可维护性、可测性等。根据模块的功能图或/及数据流图映射出方法结构是常用方法之一。

改进模块中方法的结构,降低方法间的耦合度,并提高方法的独立性以及代码可读性、效率和可维护性。优化方法结构时,要遵守以下原则:

(1)不能影响模块功能的实现。

(2)仔细考查模块或方法出错处理及模块的性能要求并进行完善。

(3)通过分解或合并方法来改进软件结构。

(4)考查方法的规模,过大的要进行分解。

(5)降低方法间接口的复杂度。

(6)不同层次的方法调用要有较合理的扇入、扇出。

(7)方法功能应可预测。

(8)提高方法内聚。(单一功能的方法内聚最高)

说明:对初步划分后的方法结构应进行改进、优化,使之更为合理。

避免使用BOOL参数。

说明:原因有二,其一是BOOL参数值无意义,TURE/FALSE的含义是非常模糊的,在调用时很难知道该参数到底传达的是什么意思;其二是BOOL参数值不利于扩充。还有NULL也是一个无意义的单词。

对于提供了返回值的方法,在引用时最好使用其返回值。

  • 效率

编程时要经常注意代码的效率。

说明:代码效率分为全局效率、局部效率、时间效率及空间效率。全局效率是站在整个系统的角度上的系统效率;局部效率是站在模块或方法角度上的效率;时间效率是程序处理输入任务所需的时间长短;空间效率是程序所需内存空间,如机器代码空间大小、数据空间大小、栈空间大小等。

在保证软件系统的正确性、稳定性、可读性及可测性的前提下,提高代码效率。

说明:不能一味地追求代码效率,而对软件的正确性、稳定性、可读性及可测性造成影响。

局部效率应为全局效率服务,不能因为提高局部效率而对全局效率造成影响。

循环体内工作量最小化。

说明:应仔细考虑循环体内的语句是否可以放在循环体之外,使循环体内工作量最小,从而提高程序的时间效率。

示例:如下代码效率不高。

c
for (ind = 0; ind < MAX_ADD_NUMBER; ind++)
{

sum += ind;

back_sum = sum; /* backup sum */

}

语句“back_sum = sum;”完全可以放在for语句之后,如下。

c
for (ind = 0; ind < MAX_ADD_NUMBER; ind++)
{

sum += ind;

}

back_sum  = sum; /* backup sum */

仔细分析有关算法,并进行优化。

仔细考查、分析系统及模块处理输入(如事务、消息等)的方式,并加以改进。

对模块中方法的划分及组织方式进行分析、优化,改进模块中方法的组织结构,提高程序效率。

说明:软件系统的效率主要与算法、处理任务方式、系统功能及方法结构有很大关系,仅在代码上下功夫一般不能解决根本问题。

编程时,要随时留心代码效率;优化代码时,要考虑周全。

不应花过多的时间拼命地提高调用不很频繁的方法代码效率。

说明:对代码优化可提高效率,但若考虑不周很有可能引起严重后果。

在多重循环中,应将最忙的循环放在最内层。

说明:减少CPU切入循环层的次数。

示例:如下代码效率不高。

c
for (row = 0; row < 100; row++)
{
for (col = 0; col < 5; col++)
{

sum += a[row][col];

}
}

可以改为如下方式,以提高效率。

c
for (col = 0; col < 5; col++)
{
for (row = 0; row < 100; row++)
{

sum += a[row][col];

}
}

尽量减少循环嵌套层次。

避免循环体内含判断语句,应将循环语句置于判断语句的代码块之中。

说明:目的是减少判断次数。循环体中的判断语句是否可以移到循环体外,要视程序的具体情况而言,一般情况,与循环变量无关的判断语句可以移到循环体外,而有关的则不可以。

示例:如下代码效率稍低。

c
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
{
if (data_type == RECT_AREA)
{

area_sum += rect_area[ind];

}

else

{

rect_length_sum += rect[ind].length;

rect_width_sum += rect[ind].width;

}
}

因为判断语句与循环变量无关,故可如下改进,以减少判断次数。

c
if (data_type == RECT_AREA)
{
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
{

area_sum += rect_area[ind];

}
}

else

{
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
{

rect_length_sum += rect[ind].length;

rect_width_sum  += rect[ind].width;

}
}

尽量用乘法或其它方法代替除法,特别是浮点运算中的除法。

说明:浮点运算除法要占用较多CPU资源。

示例:如下表达式运算可能要占较多CPU资源。

c
#define PAI 3.1416

radius = circle_length / (2 * PAI);

应如下把浮点除法改为浮点乘法。

c
#define PAI_RECIPROCAL (1 / 3.1416 ) // 编译器编译时,将生成具体浮点数

radius = circle_length * PAI_RECIPROCAL / 2;

不要一味追求紧凑的代码。

说明:因为紧凑的代码并不代表高效的机器码。

  • 异常

在界面层中尽量使用异常处理try语句,不要将系统级别的错误直接暴露给用户,而更应该的是把系统抛出的错误信息记录到LOG日志文件中去,告诉用户友好的提示信息。

注意:在Windows Form中的某个事件中的Exception要用MessageBox.Show(expt.Message);的方式弹出来,其它的要写入日志或直接向上抛出。

Windows Form 中的事件中:

csharp
private void btnAdd_Click(object sender, EventArgs e)
{

try

{

AddDeptment();

MessageBox.Show("Add AY_Deptment Success");

}

catch (Exception expt)

{

MessageBox.Show(expt.Message);

}

}

下面是在事件所要调用的方法中:

csharp
try
{

//statements;

}

catch (Exception expt) //捕获异常
{

SYSLog.WriteLog(expt.Message); //写入日志中

throw expt;

}

finally
{

//销毁对象处理

}

数据库编码规范

数据库实例

数据库名定义为系统名+模块名

全局数据库名和例程SID 名要求一致

因SID 名只能包含字符和数字,所以全局数据库名和SID 名中不能含有“_”等字符

数据库表空间

面向用户的专用数据表空间以【用户名+_+data】命名

面向用户的专用索引表空间以【用户名+_+idx】命名

面向用户的专用临时表空间以【用户名+_+tmp】命名

面向用户的专用回滚段表空间以【用户名+_+rbs 】命名

面向应用的表空间以【应用名+_data/应用名+_idx/应用名+_tmp/应用名+_rbs】命名

LOB 段数据专用表空间以其【数据表空间+_+lobs】命名,如上例中数据表空间为Aud_data,则LOB 段表空间可命名为Aud_data_lobs

表空间文件命名以表空间名+两位数序号(序号从01开始)组成,如Aud_data01

表名约定

第一位:T表、V视图、P过程、F函数、S序列

备注:主题领域,系统框架已经占用S字母,各开发小组注意回避;

另外,系统框架表以及业务接口表后面的表特性四位数字可以不受上述规则约束。

系统代码主题领域代码子主题领域
MESP计划业务
O生产业务
Q质量业务A基础
B标准
C原料(进料)
D生产过程
E实验室
I接口

数据列名称约定

关于外键约束:

外键的优点:

外键约束使得程序员更不容易将不一致性引入数据库,而且设计合适外键有助于以文档方式记录表间关系。

外键的缺点:

但这些优点是以服务器为执行必要的检查而花费额外的开销为代价的。服务器进行额外的检查会影响性能。

其次外键对并发性能的影响很大,因每次修改数据都需要去另外一个表检查数据,需要获取额外的锁(以确保事务完成之前,父表的记录不

会被删除)高并发环境下出现性能问题,更好的办法是在应用层实现外键约束。

因此,存在逻辑关系的业务表,必须在ER图建立主外键关系,但在正式库或者线上库可以不必建立物理关联关系。

数据列长度约定

主键、外键:VARCHAR2(32)

一般字符串字段:VARCHAR2(512)

长文本(如备注字段):VARCHAR2(4000)

整数类型:NUMBER(10,0)

浮点类型:NUMBER(16,6)

数据库规范操作流程

常见名称定义缩写

标准单词英文缩写英文解释
IDIDID
班次SHFTShift
保留RSRVReserve
报警ALRMAlarm
备注RMKRemark
必须NCSSNecessary
编号NONumber
标准STNDStandard
部门DEPTDepartment
采购PRCHPurchase
采购订单POPurchase Order
仓库WRHSWarehouse
测量MSRMeasure
PLYPly
产品PRDCProduct
产线LINELine
CARCar
尺寸SIZESize
处理HNDLHandle
触发TRGGTrigger
VSSLVessel
船次VOY_NOVoy No voyage number
创建CRTCreate
存储STRGStorage
代码CDCode
单位UNITUnit
到达ARRVArrival
等级GRADGrade
地点SITESite
第三THRDThird
第三方THPTThird-party
POINPoint
订单ORDROrder
定量QNTFquantify
定性QLITQualitative
SHRTShort
TONTon
发送SENDSend
范围SCOPScope
方法MTHDMethod
分类CLSFClassification
PRTParent
附加ADDTAddition
复检RINSPReinspection
复制COPYCopy
港口PORTPort
更新UPTUpdate
工厂FCTRFactory
工单WOWork order
工具TOOLTool
公式FRMLFormula
供应商SPPLSupplier
估计ESTMEstimation
规格SPECSpecification
过程PRCDProcedure
合同CNTRContract
环境ENVREnvironment
货物GOODGoods
机台MCHNMachine
计算CLCTCalculate
记录RCRDRecord
RACKRack
检测TESTTest
检验INSPInspection
阶段STGEStage
接收RECVReceive
结果RSLTResult
紧急URGUrgent
进入ENTREnter
NETNet
拒收RJCTRejection
REELReel
决策人DECS_MKRDecision-maker
客户CUSTCustomer
控制CNTLControl
扣款CHRGCharge
快检FASTFast
类别CATGCategory
类型TPType
CLMNColumn
领域AREAArea
流程PRCSProcess
路线ROUTRoute
BLNDBlind
EXMPExempted
描述DESCDescription
名称NMName
模式PTTRPattern
磨浆DFBRDefibrination
目标TRGTTarget
能源ENRGEnergy
判定DECSDecision
LOTLot
频率FRQCFrequency
品保QAQuality Assurance
品牌BRNDBrand
平均AVRGAverage
凭证PROFProof
破坏性DSTRDestructive
清洗CLENClean
取样SMPLSampling
缺陷DFCTDefect
人员STFFStaff
日期DTDate
容器CNTNContainer
上限UPPRUpper
设备EQPMEquipment
生产PRODProduction
生效TKEFTake effect
湿WETWet
湿度HMDTHumidity
时间TIMETime
识别RCGNRecognition
实际ACTLActual
实验室LABLaboratory
使用USEUse
是否YNYES OR NO
数据DATAData
数量QTYQuantity
水分MSTRMoisture
搜索SRCHSearch
速度SPEDSpeed
提单号BLNOLading Bil lNumber
天气WTHRWeather
条件CNDTCondition
停止STOPStop
通知NTFCNotification
投诉CMPLComplain
退RTRNReturn
外观EXTRExterior
完成FNSHFinish
维护MNTNMaintain
位置PLCEPlace
位置LOCTLocation
温度TMPRTemperature
物料MATMaterial
物流LGSTLogistics
系统SYSTSystem
下限LWRLower
限制LIMTLimit
相关RLTDRelated
项目ITEMITEM
消耗CNSMConsumption
SMLLSmall
小数DCMLDecimal
卸货UNLDUnload
序号SNSerial number
样品SAMPSample
业态BUBusiness Unit
异常UNSLUnusual
应用APPLApply
有效VALDvalid
原料RWMATRaw material
原始ORGNOriginal
原因RSONReason
允收ACCPacceptable
允收标准AQLAcceptable quality level
运输TRNSTransport
LONGLong
长期LNGTLong-term
执行EXCTExecute
直径DMTRDiameter
VALValue
PAPRPaper
质量QLTYQuality
种类KINDKind
重复RPTTRepetition
重量WGHTWeight
状态STTSStatus
自动AUTOAuto
GRPGroup
最大MAXMaximum
最小MINMinimum
作业WORKWork

代码简洁之道

命名

  • 名副其实

  • 变量、函数、类的名字应该是自解释性的。

  • 如果名称需要注释来补充,那就不算是名副其实。

  • 避免误导

  • 使用了多义词,因单词的多种含义引起误导。

  • 名字没有表达清楚变量、函数、类的具体含义,引发误导。

  • 变量、函数违反“单一权责原则”,代表了多个含义,具有多个功能,因此针对其中的一个功能而起的名字,就产生了误导。

  • 上下文不一致。

  • 做有意义的区分,避免使用编码

  • 没有必要把数据类型编入变量。

  • 不需要使用特殊的标记来标识成员变量。

  • 没有必要在接口的前面加I。

  • 使用读出来的名称

  • 尽量不用缩写,除非是大家都认可的。

  • 使用可搜索的名称

  • 名字长短应与其作用域大小相匹。

  • 避免思维映射,别扮可爱

  • 取的名字,不要让人再思考一遍才能明白含义。明确是王道。

  • 类名&方法名

  • 方法名应当是名词或名词短语。

  • 类名应当是名词或名词短语。

  • 使用解决方案领域名称,使用源自所涉及问题领域的名称

  • 优先使用程序员容易理解的技术性名称,其次使用问题领域名称。

  • 添加有意义的语境&不要添加没意义的语境

  • 可以通过添加语境(适当增加前缀、用类封装)的方式来增强名称的自解释性。

  • 只要短名称足够清楚,就要比长名称好。

函数

  • 长度要短(暂定50行)

  • 函数的第一规则是要短小。第二条规则是还要更短小。

  • 那函数短小到什么程度才好?每个函数都一目了然。每个函数都只说一件事。而且,每个函数都依序把你带到下一个函数。这就是函数应该达到的短小程度。

  • if语句、else语句、while语句等,其中的代码块应该只要一行。

  • 使用描述性的名称

  • 函数使用描述性的名称就是函数名称能较好的描述函数做的事。别害怕长名称。长而具有描述性的名称,要比短而令人费解的名称好。

  • 函数首字母大写。

  • 功能要单一

  • 函数应该做一件事。做好这件事,只做函数名称描述的这一件事。

  • 要无副作用

  • 函数应该像函数名称描述的那样只做自己的事,并且在被调用后不能破坏或影响其他函数调用时所要的需资源。

  • 函数中不要有太多重复的地方

  • 函数有相同重复的地方要想办法提取出公共的部分。

  • 尽量避免使用switch语句case后是函数,或者是一句话,不高于5句话

  • Switch语句最大的问题是如果有新类型出现就需要修改,这违反了开放封闭原则。其次,它明显做了不止一件事,还违反了单一职责原则。

  • 结构化编程

  • 每个函数、函数中的每个代码块都应该有一个入口、一个出口。遵循这些规则,而且永永远远不能有任何goto语句。我们赞成结构化编程的目标和规范,但对于小函数,这些规则助益不大。只有在大函数中,这些规则才会有明显的好处。

  • 使用异常替代返回错误码

注释

  • 不要用注释来美化那些糟糕的代码。

  • 尽量用代码或函数来阐述(视情况而定)。

  • 写一些有意义(版权信息、提供帮助信息、警示信息、TODO预留信息),表达准确的注释。

  • 不要写一些多余的 读起来时间较长又不能比代码本身提供更多信息,有误导性的与代码本身功能不匹配,日志式记录注释,用来标注不需要的代码、位置,篇幅太大,与代码段并无明显联系关系的。

  • 公用函数、接口等供他人使用的需添加

  • /// <summary>

csharp
/// 函数功能说明
  • /// </summary>

  • 如果要写注释就应当负责将注释保持在可维护、有关联、精确的高度。

错误处理

  • 源代码文件应该多大?

  • 好系统的单个文件,大多数200行,最多500行。

  • 并不是必须要这样,但也应该乐于接受

  • 垂直格式

  • 用一行空白行将命名空间的引入、字段的声明、属性和方法分开。

  • 紧密相关的代码应该互相靠近。

  • 如非必要,就不要把关系密切的概念放到不同的文件中。

  • 变量声明应尽可能靠近其使用位置。

  • 循环中的控制变量应该在循环语句中声明。

  • 实体变量应该在类的顶部声明。

  • 概念相关的代码应该放在一起。

  • 调用者要放在被调用者上面。

  • 一行代码应该多宽?

  • 应尽力保持代码行短小。建议80个字符,上限120个字符。

  • 原则:不要向右拖动滚动条。

  • 横向格式

  • 使用空格将相关性强的事物连接在一起,将相关性弱的事物分隔开。

  • 使用不对齐的声明和赋值。

  • 代码块的实现相对其容器代码块缩进一个层级,以此类推。

  • 短小的if语句、while循环或小函数中不推荐违反缩进规则。

  • 团队说了算

  • 一个团队应该认同一种格式风格,每个成员都应该采用那种风格。

  • 使用异常而非返回码

  • 先写Try-Catch-Finally语句

  • try catch上端必须得有,保证程序不能异常死掉。

  • try catch下端尽量不要写,除非这个异常不影响流程,或者需要抓住异常进行处理。

  • try将全部代码都包住,任何一句代码都有可能出现异常。

  • 使用不可控异常

  • 给出异常发生的环境说明

  • 应创建信息充分的错误消息,并和异常一起传递出去。

  • 消息中包括失败的操作和失败的类型。

  • 别返回null值、别传递null

  • 针对可能返回null值的方法,考虑对它再封装一层,在新方法中抛出异常或是返回特例对象。

  • 避免出现异常System.NullReferenceException:“未将对象引用设置到对象的实例。”

边界与并发

  • 使用第三方代码

  • 在接口提供者和使用者之间,存在着与生俱来的张力。

  • 第三方程序包和框架提供者追求普适性,这样就能在多个环境中工作,吸引广泛的用户。而使用者则只想根据其特定需求而使用某些接口。

  • 这种张力会导致系统边界上出现问题。

  • 使用尚不存在的代码

  • 还有另一种边界。在代码中,总有许多地方是我们的知识未及之处,例如,软件中有一个通信子系统,但该子系统的开发者还没来得及定义良好的API。

  • 这是,我们可以通过定义自己的接口API,并把该通信子系统包装起来,使得该子系统处于我们自己的控制之下。并且,还可以通过调用用此包装API以及模拟的信号输入,来模拟该通信子系统。一旦通信子系统的API被定义出来,我们就编写Adapter(适配器模式)来跨接。

  • 其他团队的子系统

  • 边界上的代码需要进行清晰的分割和定义测试,应该避免我们的代码过多的了解第三方代码中的特定信息。

  • 依靠你能控制的东西,好过依靠你控制不了的东西,免得日后受它控制。

  • 关注自己的领域,定义边界,做好上下文映射,迭代开发,拒绝大泥球!

  • 边界上下文是领域驱动(DDD)设计的一个重要模式。它是领域驱动设计中策略设计部分的关注点-这部分是关于处理大型模型和团队的。领域驱动设计通过把大型模型分割成不同的上下文并且明确定义它们之间的关系来处理大型模型。

  • 限界上下文的形象比喻:细胞膜不仅能把细胞内部和外部区分开来,而且还能决定通过的物质

  • 为什么要并发编程?

  • 解耦策略:做什么和什么时候做隔离开;

  • 误解:并发并不一定改善性能:CPU空闲时间决定

  • 更复杂的设计和代码:人类并不擅长跳跃式思维

  • 处理并发的原则:

  • 单一权责:分离并发部分代码

  • 限制数据作用域:谨慎封装、小心修改共享数据

  • 数据副本:只读副本、或者复制副本、避免修改

  • 线程独立:不要和其他线程混淆,独立数据空间

  • 定义异步方法的几点要求

  • 使用async关键字来修饰方法

  • 在异步方法中使用await关键字(不使用编译器会给出警告但不报错),否则异步方法会以同步方式执行

  • 尽量不使用void作为返回类型,若希望异步方法返回void类型,请使用Task异步方法名称以Async结尾

  • 异步方法中不能声明使用refout关键字修饰的变量

  • 异步方法执行流程

  • 在遇到awiat关键字之前,程序是按照代码顺序自上而下以同步方式执行的。 在遇到await关键字之后,系统做了以下工作:

  • 异步方法将被挂起

  • 将控制权返回给调用者

  • 使用线程池中的线程(而非额外创建新的线程)来计算await表达式的结果,所以await不会造成程序的阻塞

  • 完成对await表达式的计算之后,若await表达式后面还有代码则继续执行这些代码

  • 注释

  • 不恰当的信息, 注释只应描述有关代码和设计的技术性信息

  • 废弃的注释、糟糕的注释

  • 冗余注释,描述了已经自描述的代码

  • 注释掉的代码,别人会假定别人需要它

  • 函数

  • 过多的参数

  • 输出参数,读者更期望参数用于输入而非输出

  • 标识参数, 布尔参数宣告函数做了不止一件事

  • 死函数,永不被调用的方法应当被废弃

  • 名称

  • 名称应具有描述性,经常重新评估名称是否恰当

  • 名称应与抽象层级相符

  • 为较大作用范围选用较长名称

  • 避免编码,如 m_f 之类的前缀

  • 无歧义、名称应说明副作用

  • 一般性问题

  • 明显的行为未被实现,函数或类应该事先其他程序员有理由期待的行为。如果明显的行为未被实现,用户就不能依靠函数名称的直接,必须去阅读代码细节

  • 不正确的边界行为

  • 忽视安全。不要忽视警告,不要过后处理

  • 重复(看到重复的代码,代表遗漏了抽象)

  • 在错误的抽象层级上的代码。低层级概念和高层及概念不应该混杂在一起

  • 特性依赖(类的方法只应对其所属类中的变量和函数感兴趣,不该垂青其他类中的变量和函数)

  • 选择算子参数(函数调用中的 false/true 参数)或者 enum, integer

  • 位置错误的权责(代码应放在读者自然而然期待它所在的地方),如 PI 应放在声明三角函数的地方

  • 使用解释性变量,将计算过程打散成在用有意义的单词命名的变量中放置的中间值。

  • 把逻辑依赖改为物理依赖(依赖者模块不应当对依赖者的模块有假定,应当明确询问后者的全部信息)

  • 封装边界条件(把边界条件处理的代码集中到一处,不要散落于代码中)

  • 函数应该只在一个抽象层次上。函数中的语句应该是在同一层抽象层级上,该层级应该是函数名所示操作的下一层。

  • 较高层级上放置可配置数据,较高层级的配置型常量容易修改。

HiMind 工业互联网平台 技术文档