.NET开发规范v1.0
一、 编程规范
(一) 命名风格
-
命名要找更有表现力的词,更专业的词,比如获取数据不用get而使用fetch
-
别害怕长名称,长而具有描述性的名称比短而令人费解的名称好
-
为作用域大的名字采用更长的名字,作用域小的使用短名字
-
给变量名带上重要的细节,比如加上单位ms等。
-
【强制】严禁使用拼音与英文混合方式,避免歧义,更不允许直接使用中文的方式.注意,即使纯拼音命名方式也要避免采用
正例: alibaba / taobao / youku / shanghai (城市名称) 等国际通用的名称,可视同英文
反例: DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量 = 3
- 【强制】方法名、类名、成员变量、常量命名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO
正例: GetHttpMessage()/ MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例: getHttpMessage()/ macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
- 【强制】参数名、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式
正例: localValue / getHttpMessage() / inputUserId
-
【强制】抽象类命名使用 Abstract; 基类使用 Base 开头;异常类命名使用 Exception 结尾; 接口Interface 以I开头,实体类Entity,通用类Common,工厂Factory等具有业务意义的都以类的定义意义标识结尾,如Interface/Entity/Common/Factory;测试类命名以它要测试的类的名称开始,以 Test 结尾;
-
【强制】杜绝完全不规范的缩写,避免望文不知义
反例: AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成 condi,此类 随意缩写严重降低了代码的可阅读性
- 【推荐】如果使用到了设计模式,建议在类名中体现出具体模式。 说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
正例:
-
【推荐】保持代码的简洁性,并加上有效的 summary注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是 与接口方法相关,并且是整个应用的基础常量
-
【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部 的实现类用 Impl 的后缀与接口区别
-
【参考】枚举类名建议带上 Enum 后缀,枚举成员名称需要使用UpperCamelCase 风格。 说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
正例: 枚举名字:DealStatusEnum,成员名称:Success / UnkownReason。
- 【参考】各层命名规约:
A) Service/DAO/DLL/BLL 层方法命名规约 1) 获取单个对象的方法用 Get 做前缀 2) 获取多个对象的方法用 List 做前缀 3) 获取统计值的方法用 Count 做前缀 4) 插入的方法用 Save(推荐)或 Insert 做前缀 5) 删除的方法用 Remove(推荐)或 Delete 做前缀 6) 修改的方法用 Update 做前缀
B) 领域模型命名规约 1) 数据对象:xxxDO / xxx 即为数据表名 2) 数据传输对象:xxxDTO / xxx 为业务领域相关的名称 3) 展示对象:xxxVO / xxx / xxxViewModel 一般为网页名称
(二) 常量定义
-
【强制】不允许任何魔法值(即未经定义的常量)直接出现在代码中
-
【推荐】不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护,如:
缓存相关的常量放在类:CacheConsts 下; 系统配置相关的常量放在类:ConfigConsts 下。
说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
- 【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包 内共享常量、类内共享常量。
1) 跨应用共享常量:放置在二方库中,通常是 Common类库 中的 Constant 目录下。2) 应用内共享常量:放置在一方库的 应用项目下Modules 中的 Constant 目录下。反例:易懂变量也要统一定义成应用内共享常量;如两位工程师在两个类中分别定义了表示“是”的变量: 类 A 中:public static string YES = "yes";类 B 中:public static string YES = "y"; A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。
3) 子工程内部共享常量:即在当前子工程的 Constant 目录下。4) 包内共享常量:即在当前包下单独的 Constant 目录下。5) 类内共享常量:直接在类内部 private static final 定义。
(三) 代码格式
- 【强制】大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果是if后面没有else,也需要添加{};如果 是非空代码块则:
1) 左大括号前换行。 2) 左大括号后换行。3) 右大括号前换行。4) 右大括号后还有 else 等代码则换行;表示终止的右大括号后必须换行。
-
【强制】任何二目、三目运算符等左右都需要添加一个空格
-
【强制】if/for/switch/while等保留字与括号之间都必须加空格
-
【强制】单行字符限制不超过120个,超出需换行,换行遵循如下原则:
-
第二行相对第一行缩进4个空格,第三行换行不再缩进。
-
运算符与下文一起换行。
-
方法调用的点(.)与下文一起换行。
-
方法调用中的多个参数需换行,在逗号后进行。
-
在括号前不要换行。
正例:
-
【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格
-
【推荐】单个方法的总行数不超过80行,包括方法签名、大括号、空行、回车及不可见字符等
-
【推荐】 没必要添加空行来使代码行对齐;不同逻辑,不同业务的代码块之间插入空行来分隔。
说明:任何情形,没有必要插入多个空行来进行隔开
(四) OOP 规约
-
【强制】避免通过一个类的实例对象引用访问此类的静态方法或静态变量,无谓添加编译解析成本,直接使用类名访问即可
-
【强制】所有的重写方法override 必须添加注释说明
-
【强制】相同参数类型,相同业务含义,才能使用可变参数,不提倡使用可变参数编程,如dynamic,object等
-
【强制】调用第三方接口,提供外部使用的接口或依赖库的接口等,不允许修改方法签名,不可用随意添加参数
-
【强制】在一个方法体内调用其他方法,最多调用四层,该方法必须输出内容;每个方法体存在必须承载业务逻辑,不允许只存在传递参数而无 业务或逻辑意义的方法存在。
-
【强制】在使用一目,二目、三目运算符时,必须判断逻辑太复杂,难于理解;若存在,必须拆分逻辑,不允许写成一条判断逻辑。
反例:
-
【强制】所有的过时的类或方法后续皆不需要调用
-
【强制】所有可为空的变量在数学运算时必须做NULL检查(?)或空合并运算符(??),如:
正例:
- 【强制】所有使用空合并运算符(??)时,必须将对象用括号做起来,避免逻辑错误;如下,求和逻辑变更成空合并运算判断
反例:
-
【强制】所有返回的数组对象,在使用时都需要添加是否为空判断
-
【强制】当一个类有多个构造方法或同名方法,需要按顺序放置在一起,便于阅读。
-
【强制】类内方法定义的顺序依次是:保护方法 > 公有方法 > 私有方法 > 属性定义或常量定义
-
【强制】属性定义时,参数名称与类成员变量名称一致,在定义成员时不需要添加业务逻辑
反例:
-
【推荐】使用HashTable,Dictinary等K/V键值对时,需要注意K不能为空
-
【强制】使用集合时需要注意有序性和稳定性,避免集合的无序性和不稳定性带来的负面影响,如调试或处理异常时每次的报错信息不一样
-
【推荐】使用集合时需避免使用Distinct去重,若当前对象有未继承重写的比较方法,则需要Group(指定需比较的成员,必须具备唯一性)分组处理去重。.Net Framework 为较为复杂的比较提供了一个接口 System.Collections.IEqualityComparer,并提供了内置的实现,如 StringComparer、EqualityComparer 我们自己写的比较类也可以实现这个接口。
(五) 控制语句
-
【推荐】循环体中要考虑性能,如定义的对象,变量,获取数据等,进行不必要的try-catch操作, 输出大日志log
-
【强制】在if/else/for/while/case等语句中必须使用大括号。
-
【推荐】避免采用取反逻辑判断,取反逻辑不利于快速理解,并且取反逻辑写法必然存在正向逻辑写法
-
【参考】参数校验需要区分情况,并不是什么情况下都需要校验。 一次请求,同一个参数的校验应该只被执行一次。
(六) 注释规约
-
【强制】类、类属性、类方法的注释使用summary,备注内容; 注: 所有新增类都需要添加创建者和创建时间,后续修改者也需要备注修改者和修改时间;
-
【强制】所有的抽象类(包括接口的方法)必须要添加summary注释,除了返回值、参数、异常说明外,还需说明该方法做什么事情,实现什么功能 或应用于什么场景。
(七) 其他
- 【参考】及时清理不再使用的代码段或配置信息 说明:对于垃圾代码或过时为使用的配置,坚决清理干净,避免代码冗余
对于暂时被注释掉或暂时废弃的方法,后续可能恢复使用的代码片段,在注释时统一使用三个斜杠(///)来说明注释掉代码的缘由
- 【参考】任何数据结构的构造或初始化,需要注意指定的大小,避免数据结构无限制的增长吃光内存。
二、 异常日志
(一) 异常处理
- 【强制】 无法规避的异常,需要在调用方法外添加try-catch捕获异常,避免导致程序崩溃。try需要对不稳定的代码进行catch捕获,之后再针对性的处理异常。
说明:稳定的代码指的是无论如何都不会出错的代码。
正例: 用户注册的场景中,如用户输入非法字符,或用户名称已存在,或用户输入密码过于简单等,在后端需作出分类处理的判断,并提示用户。
- 【强制】 捕获异常是为了处理他,不要捕获了却什么都不处理或抛弃。若不想处理他,就将异常抛给调用者。最外层的使用者,必须处理移除,将其转换为用户可理解的内容。
反例:
- 【强制】 有try放到事务代码内,catch异常后,如需要回滚,一定要注意手动回滚事务
反例:
- 【推荐】 防止NPE(空引用异常),是调用者的责任,使用时必须判断是否为空。注意NPE产生的场景
1) 返回类型为基本数据类型,如:null=>int 自动拆箱抛NPE; 2) 查询结合可能为空 ;3) 对于Session,Cookies等中取出的数据,需要NPE检查; 4) 级联调用obj.GetA().GetB().GetC() 易产生NPE.
- 【参考】 避免出现重复的代码,即DRY原则
(二) 日志规约
-
【强制】 使用LogNet4记录日志,合理记录各种日志;避免不必要的日志输出,浪费磁盘空间。
-
【强制】 记录日志需要区分Release、Debug、Product 各种开发模式,Product 产品下不允许输出调试时打印的日志。
-
【强制】 Warning,Error错误日志要记录准确,便于追查错误,避免频繁报警,重复打印同一类日志信息。
三、 单元测试
- 【推荐】 好的单元测试必须遵循AIR原则,把控测试质量。好的单元测试应具有自动化、独立性、可重复执行的特点。好的单元测验应该是全自动化执行,并非交互式的。(实现单元自动化测试)
编写单元测试代码遵守BCDE原则,以保证被测试模块的交付质量。B:Border,边界值测试,包括循环边界、特殊取值。特殊时间点。数据顺序等。C:Correct,正确的输入,并得到逾期的结果。D:Design,与设计文档想结合,来编写单元测试。E:Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得到逾期的结果
四、 安全规约
-
【强制】 相关敏感信息的校验必须做;
-
【强制】 禁止向任何页面输出未经安全过滤或未经授权的用户数据
-
【强制】 部分操作必须实现正确的防重发的机制,如尝试密码操作,尝试发邮件等,需做相关的次数限制,验证码校验等。