一、命名规则
1.1 命名法说明
(1) Pascal命名法:将标识符的首字母和后面连接的每个单词的首字母都大写。
例如:BackColor
(2) Camel命名法:标识符的首字母小写,而每个后面连接的单词的首字母都大写。
例如:backColor
(3) 变量、类名、成员变量、方法等命名,禁止采用:英文和拼音组合、拼音命名、非规范的英文简写命名、汉字命名(特殊情况除外:如部分枚举采用汉字);
说明:名字定义不怕长,力求准确,并且可读性友好;
反例:decimal jiage = 0M;//价格
1.2 常见命名规范
(1) C#方法参数、内部变量命名:应采用Camel命名法,即,首字母小写;
正例:int userId = 0;
(2) C#类内成员属性:应采用Pascal命名法,即,首字母大写;
正例:public int UserId { get; set; }
(3) C#枚举(enum)命名:应采用Pascal命名法,即,首字母大写;
说明:应该为英文单词的名词单数形式,不应该增加 Enum前缀或者后缀;根据实际业务定义即可;
正例:UserType
反例:UserTypeEnum|UserTypes
(4) C#接口(interface)命名:应采用Pascal命名法,即,首字母大写;
说明:应该统一以I字母开头,实现类则去掉对应的I字母;
(5) C#实体对象(Model)命名:采用Pascal命名法,即,首字母大写;
说明:
数据库表实体应命名为ModelInfo,Model为数据库表名;
数据模型(DataModel)应该定义为DModelInfo,D为前缀,Info为后缀,Model为相关业务名称;
视图模型(ViewModel)应该定义为VModelInfo,V为前缀,Info为后缀,Model为页面或相关业务名称;
正例:TUserInfo|DUserLogInfo|VUserListInfo
(6) C#数据层(DAL)类命名:应采用Pascal命名法,即,首字母大写;
说明:应统一采用DAL后缀,ModelDAL,Model为实体名称(去掉Info后缀);
正例:TUserDAL
(7) C#持久层(Persistence,iBatisNet)命名:map文件名一般保持和文件内的namespace一致,并且和数据库实体表名一致;方法名一般和DAL层的调用方法保持一致,方便阅读;
(8) C#业务层(BLL)类命名:应采用Pascal命名法,即,首字母大写;
说明:应统一采用BLL后缀,ModelBLL,Model为实体名称(去掉Info后缀);
正例:TUserBLL|DUserLogBLL
(9) 对象类型成员的命名规则:满足类内成员属性的规则;
说明:应当以完整的类名去掉前缀和后缀之后的实体名作为属性名;
正例:public staticTUserInfo TUser { get; set; }
反例:public staticTUserInfo TUserInfo{ get; set; }
(10) 常量命名规范:全部字母大写,每个单词用下划线分割;
正例:///<summary>渠道ID(Value:175)</summary>
private const int CHANNEL_ID= 175;
(11) js变量和方法名规则:均应该采用Camel命名法,首字母小写;
(12) 数据库对象和字段的命名尽量采用Pascal命名法,即,首字母大写;
说明:常见数据库对象的命名除符合命名规范之外,应尽量使用前缀来区分对象的类型。
正例:表:TUser|视图:VUserLevel|自定义函数:GetMaxId()|存储过程:ProcSetUserState
(13) WCF引用的服务命名规范,建议引用的命名空间添加WCFService前缀;
1.3 代码文件命名
(1) 文件名遵从Pascal命名法,无特殊情况,扩展名小写;
(2) 使用统一而又通用的文件扩展名:C# 类 .cs ;
正例:TUserInfo.cs
二、代码格式化外观
2.1 代码行列展示
为了方便阅读,单行代码应尽量控制在一屏之内,否则应按照实际情况调整换行显示:
(1) 在代码或者表达式的逗号之后换行;
(2) 在代码或者表达式的操作符之前换行;
(3) 以上情况,可酌情处理,尽量保证方便阅读即可;
2.2 代码文件排版
(1) Visual Studio 可使用默认快捷键Ctrl +K,Ctrl +D 格式cs文件或cshtml等文件代码;提交代码之前应该格式化;
(2) 过于长的代码块,可按照业务逻辑相关,使用外侧代码#region...#endregion将代码分块折叠;
正例:
#region 测试方法
#endregion End 测试方法
三、程序代码注释规范
3.1 代码行内注释
(1) 因任何原因修改单行代码时,应加以注释,说明修改原因、时间和作者,如有相关需求或BUG应该同时标记需求或BUG编号;
(2) 如有必要,应该在文件最顶部增加如下格式的注释说明;并在每次重要的变更之后增加详细的变更记录说明;
3.2 文档型注释
(1) 所有供外部调用的类、方法、成员属性、枚举等都应该添加此类型的注释(summary),调用者可直接通过工具提示查阅相关说明,无需转入定义阅读代码和注释;
(2) 文档注释一般对所注释的对象的用途,使用方式,参数列表和返回值等做出详细说明,方便调用者查阅;
(3) 封装的通用js方法也可使用文档注释规范,语法应以js的注释规范为准;
说明:在Visual Studio内,js的文档注释同样具有工具提示的效果;在VS的编辑器内为js方法添加summary注释的快捷操作步骤:方法内第一行点击右键,选择“插入代码段”—选择“XMLComments”之后即可添加param或summary节点;
四、常用.NET编程规范
4.1 类和类内成员修饰符的使用
(1) 类和类内成员注意修饰符,不要滥用public关键字,依据实际情况定义;
(2) 常驻内存的常量,建议使用const修饰;
正例:
///<summary>渠道ID(Value:175)</summary>
private const int CHANNEL_ID = 175;
(3) 单例模式的实现,除使用static修饰之外,应考虑是否需要volatile修饰;
(4) partial(部分类)的使用:当一个类内代码块过多,或业务模块过于复杂时,可考虑将类拆分为多个文件保存;文件的命名应考虑指明当前类下的业务模块名称。
正例:当TUserBLL使用了partial修饰时,拆分为登录相关,后台管理相关2个部分类文件,应分别命名为:TUserBLL.Login.cs|TUserBLL.Management.cs
(5) 常见修饰符的说明:
public 公有的,类和成员的访问修饰符,无访问限制;
internal 内部的,类和成员的访问修饰符,限制为程序集内访问;
protected 受保护的,成员访问修饰符,限制为类内或派生类访问;
protected internal 受保护或内部的,成员访问修饰符,限制为:程序集内可访问、其他程序集内的派生类可访问;
private 私有的,成员访问修饰符,限制为类内访问;
说明:任何类、方法、参数、变量,严控访问范围,过于宽泛的访问范围,不利于模块解耦;
(6) 其他C#关键字的使用规范和说明,参照微软官方文档:
C# 关键字| Microsoft Docs
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/index
4.2 循环代码块的使用
(1) 循环体一定要掌握循环次数,尤其是用户级应用,避免超时;
(2) 循环体内不允许直接声明任何类型的变量(包括值类型、对象和集合类型);
说明:应该将变量定义到循环体之外,在循环内赋值;循环体内调用的其他方法也要考虑期内部变量声明的情况;
(3) js的循环体同样应该考虑性能问题;
4.3 实体模型的使用
(1) 数据库的实体模型中,增加的非数据库字段属性,应在文档型注释中标记“[虚拟字段]”,方便调用者查阅;
(2) 尽量避免匿名类的使用(包括但不限于Action的JsonResult);
说明:可使用数据表的Model、ViewModel、DataModel等通用对象作为被序列化的返回值对象;
(3) 实体中的枚举类型字段,不要定义为int获其他值类型,建议直接定义枚举类型;
正例:public static UserType Type { get; set; }
(4) 配置参数的值为Json或表中的字段为Json结构时,也应该考虑为其定义单独的数据模型,方便各应用的解析;
4.4 变量的合理赋值
(1) 字符串拼接尽量避免使用“+”或“+=”,少量拼接或格式化采用string.Format()方法,循环或大量拼接必须使用StringBuilder 对象;
说明:StringBuilder中拼接格式化的信息,可直接采用AppendFormat方法;
(2) 常数数字的赋值,非int值的情况,应在数值末尾加以类型字母标识,减少隐式的类型转换;
正例:
decimal decValue = 108.21M;//M表示decimal类型数值声明
float fltValue = 108.21F;//F表示float类型数值声明
long lgValue = 108L;//L表示long类型数值声明
(3) Hashtable的使用,在根据不同的业务条件加入不同的Key时,建议使用Key索引的形式赋值,不要采用Add()方法;
说明:
当因为业务判断导致添加了重复的Key,Add方法会抛出异常;
直接使用Key索引赋值,将会覆盖重复的Key和Value;
正例:
int userId=1;
Hashtable param =new Hashtable();
param["UserId"] =userId;
4.5 集合(IList和List)的使用
(1) 尽量减少IList对象的ToList()方法调用(尤其是集合元素较多时);
(2) 从数据源(缓存或数据库)取出的数据集合,不应该一次性取出过多数据;用户级别应用建议一次性获取不要超过100个元素;windows服务可适量增加但不建议超过1000个;业务需要时,应考虑分页处理;
4.6 MVC和Razor视图语法的规范
(1) 所有的Controller和Action都应该考虑过滤器和用户登录状态、权限的限定;
(2) 业务代码尽量后置,视图的主要目的是展示和回传数据,尽量不要出现业务代码模块;控制器主要负责和业务层交互数据,复杂业务处理尽量后置到业务层(BLL);
(3) 视图数据的展示,尽量将ViewBag等数据转换为强类型的实体,方便开发和阅读;dynamic等动态类型,无法识别和显示实体下的具体属性和文档型注释的工具提示;
(4) 通用的视图应当通过部分页或其他方式封装为通用组件;
正例:用户提交订单、用户地址管理等多处会用到省市县行政区划联动选择的代码块,应当封装一个行政区划的下拉框联动组件;
(5) 视图中某些js代码,或html代码动态加载的控制,应当尽量采用Razor的写法实现,放弃使用js的控制方式;减少页面的呈现代码量;
(6) 不建议在Action内拼接HTML字符串输出视图上的html内容,可维护和扩展性较弱;
五、持久层(基于iBatisNet)和数据库规范
(1) 所有数据库访问必须在数据层(DAL)进行,禁止在其他层访问数据库;
(2) 所有脚本只能存在于存储过程和持久层的map文件中,禁止出现在DAL或其他层;
(3) 为了方便阅读,请尽量将脚本规范化书写:
SQL SERVER关键字全部大写;
表名、字段名、视图、自定义函数等数据库对象尽量书写时和定义的大小写一致;
(4) 所有查询禁止出现“SELECT * FROM ”的写法,即便是需要全部字段也要逐个列出;如果数据库字段出现和Model不一致,“SELECT * FROM ”会导致ORM映射抛出异常;
说明:尽量缩减查询的字段数量,尤其是大文本数据的字段,非必要时不要查询返回;
(5) 查询尽量使用无锁查询 WITH ( NOLOCK );
说明:实时性极强(如账户余额、或更新后立即查询等操作)应该考虑放弃使用无锁查询;
(6) map文件的SQL脚本可根据实际参数实现动态组装,并有缓存效果,但不建议业务关联性不太强的脚本共用同一个map方法;
说明:如果书写的查询脚本存在复杂查询,JOIN级联查询等,此类脚本只能适用于确实需要JOIN或复杂查询的方法调用,若调用者不需要复杂查询,应当创建新的查询脚本方法;不可共用;
正例:已存在的脚本使用LEFT JOIN连接了多个表,当调用者不需要关联这些表时,应考虑选择其他map方法或者新创建方法,不能共用;
(7) COUNT计数时,推荐使用COUNT(1)或者COUNT(Id),不建议直接使用COUNT(*);
(8) 通过SQL脚本判断满足指定条件的记录是否存在时,推荐使用语法EXISTS;
说明:使用关键字EXISTS和IF组合,通过判断返回值是1或0来判断,要查询的数据是否存在;
IN的写法也应该使用EXISTS替代;
正例:
IF (EXISTS(SELECT 1 FROM dbo.TUser WITH ( NOLOCK ) WHERE UserId = 1 ))
SELECT 1
ELSE SELECT 0
反例:禁止直接使用以下写法:
SELECT * FROM dbo.TUser WHERE UserId = 1
SELECT TOP 1 * FROM dbo.TUser WHERE UserId =1
SELECT COUNT(1) FROM dbo.TUser WHERE UserId = 1
(9) 使用iBatisNet的Update方法,不要轻易更新整个Model的所有字段,浪费资源,并且可能会导致bug;尽量只更新已修改的字段;
说明:尽量只传入需要修改的字段,必要时可定义为Hashtable等字典类型参数;
(10) SQL SERVER 2012起,建议使用新的分页方法(OFFSET…FETCH);
说明:从执行计划来看,FETCH语法,比ROW_NUMBER()分页性能好很多,语法如下,
OFFSET { integer_constant | offset_row_count_expression } { ROW | ROWS }
FETCH { FIRST |NEXT} { integer_constant |fetch_row_count_expression } { ROW | ROWS } ONLY
(11) 使用DELETE删除数据,务必谨慎考虑;
说明:数据无价,谨慎删除!必要情况下应考虑使用逻辑删除(通过字段标识为删除状态);
(12) 事务的使用:如需使用事务,请务必确保事务有结束点;事务中只负责数据的更新操作,不应该将不必要的查询放入事务中进行;
说明:必须保证所有路径都有Commit或者RollBack,事务的使用,必须使用try…catch模块捕获异常,并在异常时RollBack;
(13) 所有的数据库脚本禁止跨库访问;
说明:所有的T-SQL语句,不要添加数据库名称前缀的形式直接跨库访问;如确切需要跨库,应使用链接服务器(LINKEDSERVER)方式,或考虑其他解决方案;
(14) 复杂的数据处理业务,应考虑创建存储过程;
(15) 生产环境一般不建议手工修改数据,如必要,请务必谨慎检查脚本的正确性之后将脚本和相关需求发送给DBA执行
六、分布式缓存使用规范
(1) 推荐使用Redis或Memcached缓存;
(2) 缓存只允许在BLL(业务层)直接引用,不可在其他层出现直接调用;
(3) 所有缓存的使用,要确保缓存数据的刷新及时性,否则会引起数据不一致的bug;
(4) 缓存的KEY命名,必须按照既定的规则定义;建议使用树形结构定义KEY节点;
(5) 使用缓存读取数据之前,务必确保配置了正确的缓存KEY,否则会导致读取数据失败;
(6) 不可对已配置的缓存中的数据私自更换数据模型,会导致其他应用无法解析到正确的数据,出现未处理的异常;
(7) 更新数据时,涉及展示数据、获取数据判断时,不可直接取缓存数据作为修改前的展示数据,防止将旧数据覆盖写入到数据库中;
(8) 合理使用缓存:相对静态的数据、访问频率较高的数据应酌情使用缓存来读取,降低数据库访问压力;并应该根据实际的缓存命中率情况考虑设定缓存的时长;
(9) Redis消息队列的使用:插入到消息队列的数据应保证正确性、并且简洁;队列由服务读取后操作,有一定的时间延迟,并且操作结果不可反馈到应用,因此要考虑实际应用场景;
七、WCF和WebAPI使用规范
7.1 封装WCF和WebAPI的原则
(1) 通用业务方法、模块应该考虑封装为WCF或WebAPI;
7.2 WCF的配置和引用
(1) 生产环境的WCF,均应该采用TCP协议部署和引用(必须要外网引用的WCF除外);
(2) 测试环境的WCF部署域名,建议使用和生产环境相同的设定(可通过hosts指向来访问),能保证调试和生产环境的一致性;
(3) 禁止通过WCF传入和返回超大量的数据;
(4) WCF的引用,应添加到BLL(业务层),不可引用至其他层;
说明:WCF虽然引用至BLL层,但配置节点应该手动添加到各个应用的Web.config或App.config中;
7.3 WCF和WebAPI的开发规范
(1) 传入的参数和返回值,尽量采用Json格式,并使用数据模型来定义,避免直接使用值类型或对象类型;
说明:值类型和对象类型的参数扩展性不好,如遇到增加返回值字段或者参数字段,不利于扩展;
(2) 如果返回的数据为集合,并且量过大,应考虑使用分页方式返回数据,每页数据建议不超过100条;
(3) 开放性的WCF接口或WebAPI务必考虑安全性控制;
说明:可通过私钥加时间戳的签名验证方式控制安全性;签名可采用MD5加密算法等实现;特殊接口如无法增加签名验证可考虑不增加该限制;有开发能力应该实现完善的OAuth2.0授权机制;
(4) 调用WCF的方法,使用完毕后务必显式的直接调用WCF的关闭方法;
说明:开启WCF的连接会消耗大量服务器资源,务必及时关闭;也不可循环多次开启连接;
(5) WCF的Service层、WebAPI的Action层尽量减少业务代码的耦合,将业务逻辑后置到BLL业务层;
(6) 通用的WCF或者WebAPI接口,建议形成开发手册(开发规范文档);
八、Visual Studio解决方案使用
(1) 所有解决方案下不得私自引入前端组件、服务端组件,不得私自变更已有的组件或框架;
(2) 通用工具层不得私自引入第三方组件,或变更、删除、新增通用方法;
(3) 所有的资源文件(CSS文件,JS文件,图片文件)不得私自修改,必须以UI提供的文件为准;
(4) 解决方案的调试,如果设置了本地IIS调试或其他特殊VS设置,请不要将配置提交到svn(或其他源代码管理工具),建议设置到user文件,并将user文件加入到svn的忽略列表;因为其他开发者肯定没有一致的IIS站点,会导致调试异常;
(5) 不得擅自升级任何解决方案下任何项目的.NETFramework版本;
(6) 不得擅自升级引入的所有第三方组件和dll文件的版本;
(7) 所有项目下引入的第三方dll文件,应该放到对应的解决方案的指定文件夹下,并分类放置、提交到svn;防止其他开发者无法获取到引入的文件,导致编译失败;
(8) 复杂项目的开发,建议先画对应的业务流程图;流程路径更严谨,开发思路更清晰,维护更方便;
(9) 项目开发过程中建议记录下发布时需要上线的文件列表,防止发布时遗漏;