本文基于 https://www.cnblogs.com/lxmajs/p/6668813.html 对其中错别字进行修正
(一)命名规约
1.【强制】代码中当且仅当私有成员可以使用下划线开始
2.【强制】代码中的命名严禁使用拼音与英文混合的方式,更不能允许直接使用中文的方式。
3.【强制】类名、类的属性、方法名、命名空间使用UpperCamelCase大写驼峰 风格,英文单词首字母大写,必须遵从驼峰形式,但以下情形例外(领域模型的相关明明)CEO / DBO 等。
正例:SysuserController / ItemInfo / TcpHelper / GetInfo()
反例:sysuserController / Iteminfo / TCPHelper / getInfo()
4.【强制】参数名、成员变量、局部变量都统一使用lowerCamelCase 小驼峰风格,除首单词外其他单词首字母大写,必须遵从驼峰形式。
正例:localCache / userList
5.【强制】常量命名全部大写,单词间用下划线隔开,力求语意表达完整清楚,不要嫌名字长。
6.【强制】抽象类命名使用 Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以它要测试的类名称开始,以Test结尾。
7.【强制】杜绝完全不规范的缩写,避免望文不知义。
8.【推荐】如果使用了设计模式,建议在类名中体现出具体模式。
说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
正例:
public class SysuserController public class OrderFactory public class TcpProxy
9.【推荐】类中的声明、方法和属性加上有效的Summery注释。
正例:
/// <summary> /// 该类是用于对用户的控制器操作类 /// </summary> public class SysuserController { /// <summary> /// 用户名 /// </summary> public string name; /// <summary> /// 获得用户名 /// </summary> Public string getName() { // return this.name; } }
10.【参考】枚举类名建议带上E前缀或Enum后缀,枚举成员名称需要全大写,单词间用下滑线隔开。
说明:枚举其实就是特殊的常量类i,切构造方法被默认强制是私有。
正例:枚举名字:EState / DealStatusEnum,成员名:SUCCESS / UNKOWN_REASON
(二)常量定义
1.【强制】不允许出现任何魔幻值(即未经定义的常量)直接出现在代码中。
//反例: string key = “cacheKey_” + user.Name; //正例: string keyPrefix = “cacheKey_”; string key = keyPrefix + user.Name;
2.【推荐】不要使用一个常量类,来维护所有的常量,应该按常量的功能进行归类,分开维护,或交由具体的业务类中定义常量。(分类存放常量)
如:缓存相关的常量放在类:CacheConstant中。系统配置相关的常量放在类:SysConfigConstant中。
说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
(三)格式规约
1.【强制】大括号的使用约定。如果大括号内为空,则简介的写成 { } 即可,不需要换行;如果是非空代码块,则左括号和右括号各占一行,内容块另起一行。
//正例: public string getName() { } public string getName() { return this.Name; } //反例: public string getName() { return this.Name; }
2.【强制】if / for / while / switch / do 等保留字与左右括号之间都必须加空格。
3.【强制】任何运算符左右必须加一个空格。
说明:运算符包括赋值运算符 = 、逻辑运算符&&、加减乘除符号、三目运算符等。
4.【强制】缩进采用4个空格,尽量不要使用tab字符。
说明:根据vs设定,tab默认已是4个空格,不需要做变动。
正例:
public static void main(string[] args) { // 缩进4个空格 string name = “Jack”; // 运算符的所有必须空一格 bool isMe = true; // 关键字if与括号之间必须有一格空格,括号内的方法体不需要空格 if (isMe == true) { Console.WriteLine(“my name is ” + name); } else { Console.WriteLine(“your name is ” + name); } }
5.【推荐】单行字符数限制不超过120个,超出需换行,换行时遵守如下原则:
1)第二行相对第一行缩进4个空格,从第三行开始不再继续缩进,参考示例。
2)运算符与下文一起换行。
3)方法调用的点符号与下文一起。
4)方法调用中多个参数需要换行,在逗号后进行换行。
5)在括号前不要换行,见反例。
// 正例: // 超过120个字符的情况下,换行缩进4个空格,并且方法钱的点符号一起换行 var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .UseApplicationInsights() .Build(); // 反例: // 超过120个字符,不要再括号前换行 var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup> ().UseApplicationInsights() .Build();
6.【强制】方法参数在定义和传入时,多个参数逗号后必须加空格。
正例:下例中实参的”a”,后边必须要有一个空格。
Method(“a”, “b”, “c”);
7.【推荐】没有必要增加若干空格来使某一行相应的字符对其。
正例:
int a = 3; long b = 4; string str = "hello world";
说明:增加str这个变量,如果对其,则给a、b都要增加几个空格,在变量比较多的情况下,是一种累赘的事情。
(四)OOP面向对象规约
1.【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可(直接用类名访问静态成员)。
2.【强制】相同的参数类型,相同的业务含义,才可以使用可变参数,避免使用Object
说明:可变参数必须放置在参数列表的最后。
正例:public void sendMessage (string msgContent, params string[] userList)
3.【强制】不能使用过时的类或方法([Obsolate]标识)
说明:C#中对于标记过时的方法,有可能会在新版本的.Net Framework中剔除,因此不建议继续使用此类或方法。
4.【强制】Object 的Equals方法容易抛空引用异常,应使用常量或确定有值得对象来调用Equals。
正例:”Jack”.Equals(user);
反例:user.Equals(“Jack”);
5.【强制】构造方法中禁止加入业务逻辑,如有初始化逻辑等,请放在Init() 方法中。
7.【强制】帮助类或业务接口类,应该使用静态方法定义接口,使用类名.方法名直接调用,避免对象声明。
正例:Helper.ToString();
反例:Helper.Instance.ToString();
7.【推荐】当一个类有多个构造方法,或多个同名方法,这些方法应该按照顺序放置在一起,便于阅读。
8.【推荐】类内方法定义顺序依次是:常量、字段、属性、方法,按照public -> protected -> private 排序。
说明:共有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是“模板设计模式”下的和新方法;二私有方法一般不需要特别关心,是一个黑盒实现,因此方法信息价值较低。
9.【推荐】循环体内,字符串的拼接方式,使用StringBulder的Append或AppendFormat方法进行扩展。
反例:
string str = “start ”; for (int i = 0; i < 100; i++) { str = str + “hello ”; }
说明:反编译出的字节码文件显示,每次循环都会new出一个StringBuilder对象,然后进行Append操作,最后通过ToString方法返回string对象,造成内存资源浪费。
10.【推荐】类成员与方法访问控制从严:
1)如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
2)工具类不允许有public或default构造方法。
3)类非static成员变量并且与子类共享,必须是protected。
4)类非static成员变量并且仅在本类使用,必须是private。
5)类static成员变量如果仅在本类使用,必须是private。
6)类成员方法只供内部调用,必须是private。
7)类成员方法只对继承类公开,那么限制为protected。
说明:任何类、方法、参数、变量,严控访问范围,过宽泛的访问范围,不利于模块解耦。
思考:如果一个private的方法,想删除就删除,可是一个public的Service方法,或者一个public的成员变量,删除一下,不得手心冒汗吗?变量像自己的小孩,尽量在自己的时限内,变量作用域太大,如果无限制的到处跑,那么你会担心的。
(五)控制语句
1.【强制】在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止:在一个switch块内,都必须包含一个default语句,并且放在最后,即使它什么代码都没有。
2.【强制】在 if / else / for / while / do 语句中都必须使用大括号,即使只有一行代码,避免使用下面的形式: if (condition) do something...
3.【推荐】尽量少使用else,if-else的方式可以改写为:
if (condition) { ... Do something; return obj; }
4.【推荐】除常用方法(如GetXXX / IsXXX)外,不要再条件判断中执行其他复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的bool变量,以提高可读性。
说明:很多if语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么如果阅读者分析逻辑表达式错误呢?
// 正例: // 伪代码如下: bool exsisted = file.Exsist() && obj != null && ...; If (exsisted) { ... } // 反例: if (file.Exsist() && obj != null && ...)
5.【推荐】循环体内的语句要考虑性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch操作(这个try-catch是否可以移至循环体外)。
6.【参考】方法中需要进行参数校验的场景:
1)调用频次低的方法。
2)执行时间开销很大的方法,参数校验时间可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那么得不偿失。
3)需要极高稳定性和可用性的方法。
4)对外提供的开放接口,不管是Api还是Http接口。
5)敏感权限入口。
7.【参考】方法中不需要参数校验的场景:
1)极有可能被循环调用的方法,不建议对参数进行校验。但在方法说明里必须注明外部参数检查要求低。
2)底层的方法调用频度都比较高,一般不校验。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底层才会暴露问题。
3)被声明成private只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参数已经做过校验或肯定不会有问题,此时可以不做校验参数。
(六)注释规约
1.【强制】类、类属性、类方法的注释必须使用C# Summary 规范,使用:
/// <summary>
/// ....
/// </summary>
格式,不得使用 //... 方式。
说明:在vs中,Summary方式会提示相关的注释,生成Summary可以正确输出相应的注释。工程调用方法是,不进入方法,即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
2.【强制】所有的抽象方法(包括接口中的方法)必须使用Summary注释,除了返回值、参数、异常说明外,还必须指出该方法做了什么事,实现了什么功能。
说明:对于子类的实现要求,或者调用注意事项,请一并说明。
3.【强制】方法内部单行注释,在被注释语句上方另起一行,使用 // 注释。方法内部多行注释使用 /* */注释,注意与代码对齐。
4.【推荐】语气“半吊子”英文来注释,不如用中文注释把问题说清楚。但专有名字与关键字保持英文原文即可。
反例:“TCP连接超时”解释成“传输控制协议连接超时”,理解反而费脑筋。
5.【推荐】代码修改的同事,注释也要进行相应的修改,预期是参数、返回值、异常、核心逻辑等的修改。
说明:代码与注释更新不同步,就像网路与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
6.【参考】注释掉的代码尽可能而配合说明,而不是简单的注释掉。
说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
7.【参考】对于注释的要求:第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息,完全没有注释的大段代码,对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能够清晰理解当时的思路;注释也是给继任者看的,使其能够快读接替自己的工作。
8.【参考】好的命名、代码结构是自解释的,注释力求精简准确,表达到位。避免出现注释的一个极端:过多滥的注释,代码逻辑一旦修改,修改注释是相当大的负担。
反例:
// put elephant into fridge
Put(elephant, fridge);
方法名put,加上两个有意义的变量名 elephant和fridge,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。
9.【参考】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
1)待办事宜(TODO):(标记人、标记时间,[预计处理时间])表示需要实现,但目前还未实现的功能。
(七)接口规约
1.【强制】业务接口的返回格式为:
{
"code": 0,
"message": "这里是返回信息",
"data": {}
}
code 标识接口返回的状态码,message 标识接口返回的信息,data 标识接口返回的数据。此三个单词均使用小写。
2.【强制】返回值code,0标识失败,1标识成功
说明:大多数系统都使用code作为接口调用成功与否的判断标志,但对于code没有统一的规范,现对于新系统发起约束,0为失败,1为成功。
(八)异常处理
1.【强制】异常不要用来做流程控制,条件控制。因为异常的处理效率比条件分支低。
2.【强制】对大段代码进行try-catch,这是不负责任的表现。catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何都不会出错的代码。对于非稳定代码的catch尽量可能的进行区分异常类型,再做对应的异常处理。
3.【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,就将该异常抛给他的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
4.【强制】有try块放到了事务代码中,catch异常后,如果要回滚事务,一定要注意手动回滚事务。
5.【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做tyr-catch。
6.【强制】捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。
7.【推荐】方法的返回值可以是null,不强制返回空集合或空对象等,必须添加注释充分说明什么情况下会返回null值。调用方进行null判断,防止NRE空引用异常问题(NullReferenceException)。
8.【推荐】防止NRE,是程序员的基本修养,注意NRE产生的场景:
1)返回类型为包装数据类型,有可能是null,返回?类型时注意判空。
2)数据库的查询结果可能为null。
3)集合是非空的,但集合里的元素有可能是null。
4)级联调用 obj.GetA().GetB().GetC(),一连串调用,容易产生NRE。
9.【推荐】在代码中使用“抛异常”还是“返回错误码”,对于公司外的http/api开放接口,必须使用“错误码”,而应用内部推荐异常抛出;跨应用间的调用,推荐使用统一的返回格式和状态码规范。
10.【参考】避免出现重复的代码
说明:随意复制和黏贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时,抽取公共方法,或者抽象公共类,甚至是公用模块。