├── README.md ├── mongodb数据库规范.md ├── LICENSE ├── 优秀程序设计的18大原则.md ├── mysql数据库规范.md ├── .gitignore └── 开发规范.md /README.md: -------------------------------------------------------------------------------- 1 | # CSharp-Coding-Standards -------------------------------------------------------------------------------- /mongodb数据库规范.md: -------------------------------------------------------------------------------- 1 | # mongodb数据库规范 2 | 3 | ## (一)mongoDB库的设计 4 | 5 | mongodb数据库命名规范 例如:公司名_模块名 FlytExpress_xxxx 6 | 7 | ### a.禁止使用任何 " _ "(下划线)外的特殊字符 8 | 9 | ### b.禁止使用数字打头的库名 10 | 11 | ### c.数据库名最多为 64字符 12 | 13 | ## (二)mongoDB集合的设计 14 | 15 | ### a.mongodb集合命名规范:模块名_业务名 rps_order 16 | 17 | ### b.集合名全部小写 18 | 19 | ### c.禁止使用任何 " _ "(下划线) 以外的特殊字符 20 | 21 | ### d.禁止使用数字打头的集合名称 22 | 23 | ### e.集合名称最多为 64字符 24 | 25 | ## (三)mongoDB文档的设计 26 | 27 | ### a.文档中的 key 禁止使用任何 " _ "(下划线)以外的特殊字符 28 | 29 | ### b.禁止使用 _id ,如:向 _id 中写入自定义内容中写入自定义内容 30 | 31 | ### c.尽量不要让数组字段成为查询条件 32 | 33 | ### e.尽量存放统一了大小写后的数据 34 | 35 | ## (四)mongoDB索引的设计 36 | 37 | ### a.优先使用覆盖索引 38 | 39 | ### b.尽量遵循最左前缀原则 40 | 41 | ### c.索引名称长度不要超过 128 字符 42 | 43 | ### d.尽可能的将单列索引并入组合索引以降低数量 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 XiaoFei Du 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /优秀程序设计的18大原则.md: -------------------------------------------------------------------------------- 1 | # The Principles of Good Programming 2 | 3 | * 避免重复原则(DRY - Don’t repeat yourself) 4 | 5 | 编程的最基本原则是避免重复。在程序代码中总会有很多结构体,如循环、函数、类等等。一旦你重复某个语句或概念,就会很容易形成一个抽象体。 6 | 7 | * 抽象原则(Abstraction Principle ) 8 | 9 | 与DRY原则相关。要记住,程序代码中每一个重要的功能,只能出现在源代码的一个位置。 10 | 11 | * 简单原则(Keep It Simple and Stupid ) 12 | 13 | 简单是软件设计的目标,简单的代码占用时间少,漏洞少,并且易于修改。 14 | 15 | * 避免创建你不要的代码 Avoid Creating a YAGNI (You aren’t going to need it) 16 | 17 | 除非你需要它,否则别创建新功能。 18 | 19 | * 尽可能做可运行的最简单的事(Do the simplest thing that could possibly work) 20 | 21 | 在编程中,一定要保持简单原则。作为一名程序员不断的反思“如何在工作中做到简化呢?”这将有助于在设计中保持简单的路径。 22 | 23 | * 别让我思考(Don’t make me think ) 24 | 25 | 这是Steve Krug一本书的标题,同时也和编程有关。所编写的代码一定要易于读易于理解,这样别人才会欣赏,也能够给你提出合理化的建议。相反,若是繁杂难解的程序,其他人总是会避而远之的。 26 | 27 | * 开闭原则(Open/Closed Principle) 28 | 29 | 你所编写的软件实体(类、模块、函数等)最好是开源的,这样别人可以拓展开发。不过,对于你的代码,得限定别人不得修改。换句话说,别人可以基于你的代码进行拓展编写,但却不能修改你的代码。 30 | 31 | * 代码维护(Write Code for the Maintainer) 32 | 33 | 一个优秀的代码,应当使本人或是他人在将来都能够对它继续编写或维护。代码维护时,或许本人会比较容易,但对他人却比较麻烦。因此你写的代码要尽可能保证他人能够容易维护。用书中原话说“如果一个维护者不再继续维护你的代码,很可能他就有想杀了你的冲动。” 34 | 35 | * 最小惊讶原则(Principle of least astonishment) 36 | 37 | 最小惊讶原则通常是在用户界面方面引用,但同样适用于编写的代码。代码应该尽可能减少让读者惊喜。也就是说,你编写的代码只需按照项目的要求来编写。其他华丽的功能就不必了,以免弄巧成拙。 38 | 39 | * 单一责任原则(Single Responsibility Principle) 40 | 41 | 某个代码的功能,应该保证只有单一的明确的执行任务。 42 | 43 | * 低耦合原则(Minimize Coupling) 44 | 45 | 代码的任何一个部分应该减少对其他区域代码的依赖关系。尽量不要使用共享参数。低耦合往往是完美结构系统和优秀设计的标志。 46 | 47 | * 最大限度凝聚原则(Maximize Cohesion) 48 | 49 | 相似的功能代码应尽量放在一个部分。 50 | 51 | * 隐藏实现细节(Hide Implementation Details) 52 | 53 | 隐藏实现细节原则,当其他功能部分发生变化时,能够尽可能降低对其他组件的影响。 54 | 55 | * 迪米特法则/最少知识原则(Law of Demeter) 56 | 57 | 该代码只和与其有直接关系的部分连接。(比如:该部分继承的类,包含的对象,参数传递的对象等)。 58 | 59 | * 避免过早优化(Avoid Premature Optimization) 60 | 61 | 除非你的代码运行的比你想像中的要慢,否则别去优化。假如你真的想优化,就必须先想好如何用数据证明,它的速度变快了。 62 | 63 | “过早的优化是一切罪恶的根源”——Donald Knuth 64 | 65 | * 代码重用原则(Code Reuse is Good) 66 | 67 | 重用代码能提高代码的可读性,缩短开发时间。 68 | 69 | * 关注点分离(Separation of Concerns) 70 | 71 | 不同领域的功能,应该由不同的代码和最小重迭的模块组成。 72 | 73 | * 拥抱改变(Embrace Change) 74 | 75 | 这是Kent Beck一本书的标题,同时也被认为是极限编程和敏捷方法的宗旨。 76 | 77 | 许多其他原则都是基于这个概念的,即你应该积极面对变化。事实上,一些较老的编程原则如最小化耦合原则都是为了使代码能够容易变化。无论你是否是个极限编程者,基于这个原则去编写代码会让你的工作变得更有意义。 78 | -------------------------------------------------------------------------------- /mysql数据库规范.md: -------------------------------------------------------------------------------- 1 | # 数据库规范 2 | 3 | ## (一) 建表规范 4 | 5 | ### 1. 【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint 6 | 7 | (1 表示是, 0 表示否),此规则同样适用于 odps 建表。 8 | 说明: 任何字段如果为非负数,必须是 unsigned。 9 | 10 | ### 2. 【强制】表名、字段名必须使用小写字母或数字;禁止出现数字开头,禁止两个下划线中间只 11 | 12 | 出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。 13 | 正例: getter_admin, task_config, level3_name 14 | 反例: GetterAdmin, taskConfig, level_3_name 15 | 16 | ### 3. 【强制】表名不使用复数名词。 17 | 18 | 说明: 表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数 19 | 形式,符合表达习惯。 20 | 21 | ### 4. 【强制】禁用保留字,如 desc、 range、 match、 delayed 等, 参考官方保留字。 22 | 23 | ### 5. 【强制】唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。 24 | 25 | 说明: uk_ 即 unique key; idx_ 即 index 的简称。 26 | 27 | ### 6. 【强制】小数类型为 decimal,禁止使用 float 和 double。 28 | 29 | 说明: float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 30 | 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。 31 | 32 | ### 7. 【强制】如果存储的字符串长度几乎相等,使用 CHAR 定长字符串类型。 33 | 34 | ### 8. 【强制】 varchar 是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为 TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率。 35 | 36 | ### 9. 【强制】表必须编写表名,字段comment说明。 37 | 38 | 正例: `order_id` int(11) NOT NULL COMMENT '订单id'。 39 | 40 | ### 10.【推荐】表的命名最好是加上“业务名称_表的作用” ,避免上云梯后,再与其它业务表关联时有混淆。 41 | 42 | 正例: tiger_task / tiger_reader / mpp_config 43 | 44 | ### 11.【推荐】库名与应用名称尽量一致。 45 | 46 | ### 12.【推荐】如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。 47 | 48 | ### 13.【推荐】字段允许适当冗余,以提高性能,但是必须考虑数据同步的情况。冗余字段应遵循: 49 | 50 | #### 1)不是频繁修改的字段。 51 | 52 | #### 2)不是 varchar 超长字段,更不能是 text 字段。 53 | 54 | 正例: 各业务线经常冗余存储商品名称,避免查询时需要调用 IC 服务获取。 55 | 56 | ### 14.【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 57 | 58 | 说明: 如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。 59 | 反例: 某业务三年总数据量才 2 万行,却分成 1024 张表,问:你为什么这么设计?答:分 1024张表,不是标配吗? 60 | 61 | ### 15.【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。 62 | 63 | 正例: 人的年龄用 unsigned tinyint(表示范围 0-255,人的寿命不会超过 255 岁);海龟就必须是 smallint,但如果是太阳的年龄,就必须是 int;如果是所有恒星的年龄都加起来,那么就必须使用 bigint。 64 | 65 | ## (二) 索引规范 66 | 67 | ### 1. 【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。 68 | 69 | 说明: 不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验和控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。 70 | 71 | ### 2. 【强制】超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引。 72 | 73 | 说明: 即使双表 join 也要注意表索引、 SQL 性能。 74 | 75 | ### 3. 【强制】在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。 76 | 77 | 说明: 索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达 90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。 78 | 79 | ### 4. 【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。 80 | 81 | 说明: 索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。 82 | 83 | ### 5. 【推荐】如果有 order by 的场景,请注意利用索引的有序性。 order by 最后的字段是组合索 84 | 85 | 引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。 86 | 正例: where a=? and b=? order by c; 索引: a_b_c 87 | 反例: 索引中有范围查找,那么索引有序性无法利用,如: WHERE a>10 ORDER BY b; 索引 a_b 88 | 无法排序。 89 | 90 | ### 6. 【推荐】利用覆盖索引来进行查询操作,来避免回表操作。 91 | 92 | 说明: 如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。 93 | 正例: IDB 能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的一种效果,用 explain 的结果, extra 列会出现: using index. 94 | 95 | ### 7. 【推荐】利用延迟关联或者子查询优化超多分页场景。 96 | 97 | 说明: MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。 98 | 正例: 先快速定位需要获取的 id 段,然后再关联: 99 | SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id 100 | 101 | ### 8. 【推荐】 SQL 性能优化的目标:至少要达到 range 级别, 要求是 ref 级别, 如果可以是 consts最好。 102 | 103 | 说明: 104 | 105 | #### 1)consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。 106 | 107 | #### 2) ref 指的是使用普通的索引。(normal index) 108 | 109 | #### 3) range 对索引进范围检索。 110 | 111 | 反例: explain 表的结果, type=index,索引物理文件全扫描,速度非常慢,这个 index 级别 112 | 比较 range 还低,与全表扫描是小巫见大巫。 113 | 114 | ### 9. 【推荐】建组合索引的时候,区分度最高的在最左边。 115 | 116 | 正例: 如果 where a=? and b=? , a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。 117 | 118 | 说明: 存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如: where a>?and b=? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。 119 | 120 | ### 10.【参考】创建索引时避免有如下极端误解: 121 | 122 | #### 1)误认为一个查询就需要建一个索引。 123 | 124 | #### 2)误认为索引会消耗空间、严重拖慢更新和新增速度。 125 | 126 | #### 3)误认为唯一索引一律需要在应用层通过“先查后插” 方式解决。 127 | 128 | ## (三) SQL 规范 129 | 130 | ### 1. 【强制】不要使用 count(列名)或 count(常量)来替代 count(*), count(*)就是 SQL92 定义的 131 | 132 | 标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 133 | 说明: count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。 134 | 135 | ### 2. 【强制】 count(distinct col) 计算该列除 NULL 之外的不重复数量。注意 count(distinctcol1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。 136 | 137 | ### 3. 【强制】当某一列的值全是 NULL 时, count(col)的返回结果为 0,但 sum(col)的返回结果为NULL,因此使用 sum()时需注意 NPE 问题。 138 | 139 | 正例: 可以使用如下方式来避免 sum 的 NPE 问题: SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM 140 | table; 141 | 142 | ### 4. 【强制】使用 ISNULL()来判断是否为 NULL 值。注意: NULL 与任何值的直接比较都为 NULL。 143 | 144 | 说明: 145 | 146 | #### 1) NULL<>NULL 的返回结果是 NULL,不是 false。 147 | 148 | #### 2) NULL=NULL 的返回结果是 NULL,不是 true。 149 | 150 | #### 3) NULL<>1 的返回结果是 NULL,而不是 true。 151 | 152 | ### 5. 【强制】在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。 153 | 154 | ### 6. 【强制】不得使用外键与级联,一切外键概念必须在应用层解决。 155 | 156 | 说明: (概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,则为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。 157 | 158 | ### 7. 【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。 159 | 160 | ### 8. 【强制】 IDB 数据订正时,删除和修改记录时,要先 select,避免出现误删除,确认无误才能提交执行。 161 | 162 | ### 9. 【推荐】 in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。 163 | 164 | ### 10.【参考】因全球化需要,所有的字符存储与表示,均以 utf-8 编码,那么字符计数方法注意: 165 | 166 | 说明: 167 | SELECT LENGTH("飞特物流"); 返回为 12 168 | SELECT CHARACTER_LENGTH("飞特物流"); 返回为 4 169 | 如果要使用表情,那么使用 utfmb4 来进行存储,注意它与 utf-8 编码。 170 | 171 | ### 11.【参考】 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 172 | 173 | 无事务且不触发 trigger,有可能造成事故,故不建议在开发代码中使用此语句。 174 | 说明: TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。 175 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Visual Studio Code cache/options directory 30 | .vscode/ 31 | # Uncomment if you have tasks that create the project's static files in wwwroot 32 | #wwwroot/ 33 | 34 | # MSTest test Results 35 | [Tt]est[Rr]esult*/ 36 | [Bb]uild[Ll]og.* 37 | 38 | # NUNIT 39 | *.VisualState.xml 40 | TestResult.xml 41 | 42 | # Build Results of an ATL Project 43 | [Dd]ebugPS/ 44 | [Rr]eleasePS/ 45 | dlldata.c 46 | 47 | # Benchmark Results 48 | BenchmarkDotNet.Artifacts/ 49 | 50 | # .NET Core 51 | project.lock.json 52 | project.fragment.lock.json 53 | artifacts/ 54 | **/Properties/launchSettings.json 55 | 56 | *_i.c 57 | *_p.c 58 | *_i.h 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.svclog 79 | *.scc 80 | 81 | # Chutzpah Test files 82 | _Chutzpah* 83 | 84 | # Visual C++ cache files 85 | ipch/ 86 | *.aps 87 | *.ncb 88 | *.opendb 89 | *.opensdf 90 | *.sdf 91 | *.cachefile 92 | *.VC.db 93 | *.VC.VC.opendb 94 | 95 | # Visual Studio profiler 96 | *.psess 97 | *.vsp 98 | *.vspx 99 | *.sap 100 | 101 | # Visual Studio Trace Files 102 | *.e2e 103 | 104 | # TFS 2012 Local Workspace 105 | $tf/ 106 | 107 | # Guidance Automation Toolkit 108 | *.gpState 109 | 110 | # ReSharper is a .NET coding add-in 111 | _ReSharper*/ 112 | *.[Rr]e[Ss]harper 113 | *.DotSettings.user 114 | 115 | # JustCode is a .NET coding add-in 116 | .JustCode 117 | 118 | # TeamCity is a build add-in 119 | _TeamCity* 120 | 121 | # DotCover is a Code Coverage Tool 122 | *.dotCover 123 | 124 | # AxoCover is a Code Coverage Tool 125 | .axoCover/* 126 | !.axoCover/settings.json 127 | 128 | # Visual Studio code coverage results 129 | *.coverage 130 | *.coveragexml 131 | 132 | # NCrunch 133 | _NCrunch_* 134 | .*crunch*.local.xml 135 | nCrunchTemp_* 136 | 137 | # MightyMoose 138 | *.mm.* 139 | AutoTest.Net/ 140 | 141 | # Web workbench (sass) 142 | .sass-cache/ 143 | 144 | # Installshield output folder 145 | [Ee]xpress/ 146 | 147 | # DocProject is a documentation generator add-in 148 | DocProject/buildhelp/ 149 | DocProject/Help/*.HxT 150 | DocProject/Help/*.HxC 151 | DocProject/Help/*.hhc 152 | DocProject/Help/*.hhk 153 | DocProject/Help/*.hhp 154 | DocProject/Help/Html2 155 | DocProject/Help/html 156 | 157 | # Click-Once directory 158 | publish/ 159 | 160 | # Publish Web Output 161 | *.[Pp]ublish.xml 162 | *.azurePubxml 163 | # Note: Comment the next line if you want to checkin your web deploy settings, 164 | # but database connection strings (with potential passwords) will be unencrypted 165 | *.pubxml 166 | *.publishproj 167 | 168 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 169 | # checkin your Azure Web App publish settings, but sensitive information contained 170 | # in these scripts will be unencrypted 171 | PublishScripts/ 172 | 173 | # NuGet Packages 174 | *.nupkg 175 | # The packages folder can be ignored because of Package Restore 176 | **/[Pp]ackages/* 177 | # except build/, which is used as an MSBuild target. 178 | !**/[Pp]ackages/build/ 179 | # Uncomment if necessary however generally it will be regenerated when needed 180 | #!**/[Pp]ackages/repositories.config 181 | # NuGet v3's project.json files produces more ignorable files 182 | *.nuget.props 183 | *.nuget.targets 184 | 185 | # Microsoft Azure Build Output 186 | csx/ 187 | *.build.csdef 188 | 189 | # Microsoft Azure Emulator 190 | ecf/ 191 | rcf/ 192 | 193 | # Windows Store app package directories and files 194 | AppPackages/ 195 | BundleArtifacts/ 196 | Package.StoreAssociation.xml 197 | _pkginfo.txt 198 | *.appx 199 | 200 | # Visual Studio cache files 201 | # files ending in .cache can be ignored 202 | *.[Cc]ache 203 | # but keep track of directories ending in .cache 204 | !*.[Cc]ache/ 205 | 206 | # Others 207 | ClientBin/ 208 | ~$* 209 | *~ 210 | *.dbmdl 211 | *.dbproj.schemaview 212 | *.jfm 213 | *.pfx 214 | *.publishsettings 215 | orleans.codegen.cs 216 | 217 | # Since there are multiple workflows, uncomment next line to ignore bower_components 218 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 219 | #bower_components/ 220 | 221 | # RIA/Silverlight projects 222 | Generated_Code/ 223 | 224 | # Backup & report files from converting an old project file 225 | # to a newer Visual Studio version. Backup files are not needed, 226 | # because we have git ;-) 227 | _UpgradeReport_Files/ 228 | Backup*/ 229 | UpgradeLog*.XML 230 | UpgradeLog*.htm 231 | 232 | # SQL Server files 233 | *.mdf 234 | *.ldf 235 | *.ndf 236 | 237 | # Business Intelligence projects 238 | *.rdl.data 239 | *.bim.layout 240 | *.bim_*.settings 241 | 242 | # Microsoft Fakes 243 | FakesAssemblies/ 244 | 245 | # GhostDoc plugin setting file 246 | *.GhostDoc.xml 247 | 248 | # Node.js Tools for Visual Studio 249 | .ntvs_analysis.dat 250 | node_modules/ 251 | 252 | # TypeScript v1 declaration files 253 | typings/ 254 | 255 | # Visual Studio 6 build log 256 | *.plg 257 | 258 | # Visual Studio 6 workspace options file 259 | *.opt 260 | 261 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 262 | *.vbw 263 | 264 | # Visual Studio LightSwitch build output 265 | **/*.HTMLClient/GeneratedArtifacts 266 | **/*.DesktopClient/GeneratedArtifacts 267 | **/*.DesktopClient/ModelManifest.xml 268 | **/*.Server/GeneratedArtifacts 269 | **/*.Server/ModelManifest.xml 270 | _Pvt_Extensions 271 | 272 | # Paket dependency manager 273 | .paket/paket.exe 274 | paket-files/ 275 | 276 | # FAKE - F# Make 277 | .fake/ 278 | 279 | # JetBrains Rider 280 | .idea/ 281 | *.sln.iml 282 | 283 | # CodeRush 284 | .cr/ 285 | 286 | # Python Tools for Visual Studio (PTVS) 287 | __pycache__/ 288 | *.pyc 289 | 290 | # Cake - Uncomment if you are using it 291 | # tools/** 292 | # !tools/packages.config 293 | 294 | # Tabs Studio 295 | *.tss 296 | 297 | # Telerik's JustMock configuration file 298 | *.jmconfig 299 | 300 | # BizTalk build output 301 | *.btp.cs 302 | *.btm.cs 303 | *.odx.cs 304 | *.xsd.cs 305 | 306 | # OpenCover UI analysis results 307 | OpenCover/ 308 | -------------------------------------------------------------------------------- /开发规范.md: -------------------------------------------------------------------------------- 1 | # DotNet开发规范 2 | 3 | ## 一、代码规范 4 | 5 | * 默认值使用属性控制而不是在业务代码中处理 6 | 7 | * 集合类型不允许为空 8 | 9 | 原因:在代码质量未保证以及新的语法糖(order.Details?.Any())未普及前,可避免绝大多数的空指针异常。而且可以减少很大部分nullable的判断及其类型转换。 10 | 11 | * 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。 12 | 13 | * 所有的覆写方法,必须加Override关键字。 14 | 15 | 原因:添加Override关键字,编译器可判断是否覆写成功,另外抽象类如果对方法名进行修改,实现类可以马上编译报错。 16 | 17 | * 方法参数如果不是相同业务含义,禁止使用params关键字。另外提倡直接使用集合来传递相同业务含义的参数而不是用params关键字。 18 | 19 | * 禁止使用过时的类和方法。接口提供方要明确过时接口,接口调用方有义务考证过时方法的新实现。 20 | 21 | 说明:如new MongoClient(connStr).GetServer().GetDatabase(database)官方标记为过时,则要使用new MongoClient(connStr).GetDatabase(database)来代替。 22 | 23 | 【正例】 24 | 25 | ```CSharp 26 | public MongoDbService(MongoDbConfig config) 27 | { 28 | var connStr = 29 | $"mongodb://{config.UserName}:{config.Password}@{config.Server}:{config.Port}/{config.Database}"; 30 | InitMember(connStr, config.Database); 31 | } 32 | 33 | private void InitMember(string connStr, string database) 34 | { 35 | var client = new MongoClient(connStr); 36 | MongoDatabase = client.GetDatabase(database); 37 | } 38 | ``` 39 | 40 | * 内部服务不要覆盖异常信息并以自定义信息替换 41 | 42 | 说明:会导致原有异常被覆盖并导致要查看并调试代码才能排查问题,并且会覆盖异常信息的堆栈跟踪,如果是为了友好提示则由框架封装处理(MVC、WebApi、TopShelf等) 43 | 44 | 【反例】 45 | 46 | ```CSharp 47 | try 48 | { 49 | var result = PostRequestHelper.Post(postUrl, paramDic); 50 | var aeOrderDetail = JsonHelper.JsonToObject(result); 51 | return aeOrderDetail.Success; 52 | } 53 | catch (WebException e) 54 | { 55 | var message = SalesPlatformException.ParserException(e); 56 | throw new Exception(message); 57 | } 58 | ``` 59 | 60 | * 优先使用已存在的功能,禁止自己另外实现一套逻辑。 61 | 62 | 【正例】 63 | 64 | ```CSharp 65 | var sqlNames = string.Join(",", names); 66 | ``` 67 | 68 | 【反例】 69 | 70 | ```CSharp 71 | var sqlNames = string.Empty; 72 | foreach(var i = 0; i < names.Length ; i++) 73 | { 74 | sqlNames += name[i]; 75 | if(i < names.Length - 1) 76 | sqlNames += ","; 77 | } 78 | ``` 79 | 80 | * 字符串拼接使用StringBuilder,直接使用字符串拼接会占用字符串常量池并导致无效的字符串常量池占用。 81 | 82 | 【正例】 83 | 84 | ```CSharp 85 | var msg = new StringBuilder("以下账号未激活:"); 86 | var unactivatedUsers = unauthorizedUsers.Concat(blockedUsers); 87 | msg.Append(string.Join(",",users.Select(p => p.Name).ToList())) 88 | ``` 89 | 90 | 【反例】 91 | 92 | ```CSharp 93 | var msg = "以下账号未激活:"; 94 | msg += string.Join(",", unauthorizedUsers.Select(p => p.Name).ToList()); 95 | msg += ","; 96 | msg += string.Join(",", blockedUsers.Select(p => p.Name).ToList()); 97 | ``` 98 | 99 | * 当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便于阅读。 100 | 101 | * 类内方法定义顺序依次是:属性 > 公有方法 > 保护方法 > 私有方法。 102 | 103 | 说明:公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个黑盒实现。 104 | 105 | * 关联的属性和字段放一起。 106 | 107 | 【正例】 108 | 109 | ```CSharp 110 | private Guid _id; 111 | public Guid Id 112 | { 113 | get => _id; 114 | private set => _id = value; 115 | } 116 | 117 | private string _name; 118 | public string Name 119 | { 120 | get => _name; 121 | private set => _name = value; 122 | } 123 | ``` 124 | 125 | * 可为静态方法的方法,就声明为静态方法。 126 | 127 | 说明:调用静态方法可以不用实例化对象。 128 | 129 | * 类成员与方法访问控制从严: 130 | 131 | * 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。 132 | 133 | * 类非 static 成员变量并且与子类共享,必须是 protected。 134 | 135 | * 类非 static 成员变量并且仅在本类使用,必须是 private。 136 | 137 | * 类 static 成员变量如果仅在本类使用,必须是 private。 138 | 139 | * 若是 static 成员变量,必须考虑是否为 readonly 甚至 const。 140 | 141 | * 类成员方法只供类内部调用,必须是 private。 142 | 143 | * 类成员方法只对继承类公开,那么限制为 protected。 144 | 145 | 说明:任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。 146 | 147 | * 多个互斥的状态直接使用枚举来设计,而不是用多个布尔值来描述。 148 | 149 | * 禁止出现任何魔法数字,代之以有名字的枚举或者布尔值 150 | 151 | 说明:所魔法数字,在编程领域指的是莫名其妙出现的数字。数字的意义必须通过阅读代码才能推断出来。 152 | 153 | 【反例】 154 | 155 | ```CSharp 156 | var deductTime = order.IsBangGoodCustomer == 1 ? jjDate.AddHours(1) : pkgTime; 157 | 158 | switch(i) 159 | { 160 | case 0: doSomething; 161 | case 1: doSomething; 162 | case 2: doSomething; 163 | case 3: doSomething; 164 | } 165 | ``` 166 | 167 | * 用于计算的浮点优先使用decimal,因为用double会出现精度丢失问题。但在性能测试中double比decimal快一个数量级,因此要考虑业务的具体需要选择使用。 168 | 169 | 例子: 170 | 171 | ```CSharp 172 | var a = 3; 173 | var b = 0.03; 174 | var c = 0.03; 175 | var result = a + b + c; 176 | ``` 177 | 178 | result的结果为3.0599999999999996而不是预想中的3.06 179 | 180 | 性能测试结果(100万次循环): 181 | 182 | |Type|Time|CPU Cycles| 183 | |-|-|-| 184 | |int|20ms|53,130,304| 185 | |long|73ms|224,594,996| 186 | |float|13ms|41,020,292| 187 | |double|23ms|72,120,164| 188 | |decimal|405ms|1,234,385,590| 189 | 190 | * 主键只允许三种类型,GUID、snowflake生成的long以及流水号,禁止使用RDB的自增整型作为主键。 191 | 192 | 原因:使用RDB的自增整型存在单点问题,难以做到高可用和横向扩展,甚至可能要等到持久化后才可以获取到Id,在分布式系统中难以用于进行幂等处理。通常流水号存在业务需要因此允许以RDB的自增作为种子生成来使用,snowflake的long类型可分布式生成,但要考虑集群中机器号的设计。如果使用GUID类型,禁止使用系统的Guid.NewId()生成,必须使用GuidHelper来获取,因为针对不同RDB的GUID处理,GuidHelper生成的有序GUID可在对应的RDB上提供Int级别的性能。[详情](http://www.codeproject.com/Articles/388157/GUIDs-as-fast-primary-keys-under-multiple-database) 193 | 194 | * 业务对象尽量使用DateTimeOffset而不是DateTime,但是DTO或者PO对象由于序列化器/持久化的技术选型,可能并不支持DateTimeOffset(如protobuf不支持DateTimeOffset,MySql也不支持DateTimeOffset类型),这里要针对具体的技术进行选择。如使用DateTime,但默认全部均为UTC。 195 | 196 | 原因:DateTime本身存在设计缺陷,如以下代码: 197 | 198 | ```CSharp 199 | var d = DateTime.Now; 200 | var d2 = d.ToUniversalTime(); 201 | d == d2 //false 202 | d.Equals(d2); //false 203 | ``` 204 | 205 | > d和d2本质上是不同时区的同一时间,两者应该相等,但DateTime并没有保存时区信息,而是通过DateTimeKind来表达本地时间和UTC时间,并且当这个枚举为Unspecified时则更有歧义。 206 | > 在[微软的文档](https://docs.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime)中已经建议使用DateTimeOffset来代替DateTime。DateTimeOffset 207 | > 实际上是一个基于UTC的偏移量,同时保存了时区的信息,这有助于我们统一系统中的时间格式以及时间判断。 208 | > 但是要注意,某些序列化方式不支持DateTimeOffset,因此在DTO/PO等类型中可以使用DateTime或者Timaspan来进行处理,而聚合(DO)中还是尽量使用DateTimeOffset。 209 | 210 | ## 二、集合处理 211 | 212 | * 熟练掌握lambda表达式和linq,优先使用lambda表达式。 213 | 214 | 说明:lambda表达式将声明式语言引入到C#中,使代码更加优雅健壮。lambda后微软出于商业利益想实现一种类sql语法的语法糖(一开始甚至和sql写法一样是 select u.Name from user,但出于智能提示的考虑改为现在的写法 from u in user select u.Name),因此linq和lambda是功能等价的。但实际上lambda链式编程的写法更加简单易读书写方便,linq相对于lambda的优势在于类sql语法比较容易写出查询数据库的表达式语句。但在CQS的设计下Q端查询数据库使用dapper甚至ado.net等更加简单快捷,因此linq使用的范围更加狭窄。 215 | 216 | * 使用SelectMany代替双重foreach取值 217 | 218 | 【正例】 219 | 220 | ```CSharp 221 | var kidNodeIds = categories.SelectMany(p => p.KidNodes.Select(g => g.Id)); 222 | ``` 223 | 224 | 【反例】 225 | 226 | ```CSharp 227 | var kidNodeIds = new List(); 228 | foreach(var category in categories) 229 | foreach(var kidNode in category.KidNodes) 230 | kidNodeIds.Add(kidNode.Id); 231 | ``` 232 | 233 | * 了解IEnumerable和IQueryable的区别,IQueryable中不许出现任何函数调用 234 | 235 | 说明:IQueryable的数据源千差万别,除了RDB、NoSql甚至可以是WebApi等远程调用,表达式树的解析完全依赖于provider的实现,因此如果在表达式中写函数调用,编译不会报错,但可能会报运行时异常(如NotSupportException) 236 | 237 | 【反例】 238 | 239 | ```CSharp 240 | var query = dataContext.User; 241 | var result = query 242 | .Where(u => u.Name == name.ToLower() || u.CategoryId == CategoryId.ToString()); 243 | ``` 244 | 245 | * 禁止无用的查询字段以及排序字段 246 | 247 | 索引的创建以及维护是需要成本的,当一个查询中使用了多余的字段,要么影响其查询速度,要么为了此查询功能而污染了索引的设计并导致拖慢整个系统的响应。 248 | 249 | 在一个完整的系统中,数据库往往最容易成为瓶颈,最难以横向扩展,造成影响也是最大的。因此对于数据库我们要避免滥用其功能,如在一个非分页的查询中使用数据库排序。 250 | 251 | 【反例】 252 | 253 | ```CSharp 254 | var result = dbContext 255 | .Where(order => order.Status == OrderStatus.Commited) 256 | .OrderBy(order => order.ImportTime) 257 | .ToList(); 258 | ``` 259 | 260 | ## 三、并发处理 261 | 262 | * 获取单例对象要线程安全。在单例对象里面做操作也要保证线程安全。 263 | 264 | 说明:资源驱动类、工具类、单例工厂类都需要注意。 265 | 266 | * 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 267 | 268 | 说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。 269 | 270 | * 在一些存在线程安全的地方,可以考虑用线程安全的集合代替非线程安全的集合。 271 | 272 | 说明:如用ConcurrentDictionary代替Dictionary。 273 | 274 | * 禁止使用静态字段作为本地缓存,如果使用了则必须实现订阅刷新功能。 275 | 276 | 说明:任何服务以及站点都要站在集群的角度考虑,而不能自以为现在只发布一个Host就随便实现。特别是站点项目,虽然在IIS上只发布了一个站点,但如果设置了多个工作进程,每个进程间实际上是单独的Host,除了静态字段外单例也只是在同一个Host中起作用。 277 | 278 | ## 四、控制语句 279 | 280 | * 在一个 switch 块内,每个 case 要么通过 break/return 来终止,要么注释说明程序 将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有。 281 | 282 | * 尽量少用else,甚至可以考虑反转if来减少层级嵌套,如果if else超过3层请使用状态设计模式。 283 | 284 | 【反例1】 285 | 286 | ```CSharp 287 | if(_conn == null) 288 | { 289 | lock (LockObj) 290 | { 291 | DoSomething... 292 | if(_conn == null) 293 | _conn = factory.CreateConnection(); 294 | return _conn; 295 | } 296 | } 297 | ``` 298 | 299 | 【正例1】 300 | 301 | ```CSharp 302 | if (_conn != null) return; 303 | lock (LockObj) 304 | { 305 | DoSomething... 306 | return _conn ?? (_conn = factory.CreateConnection()); 307 | } 308 | ``` 309 | 310 | 【反例2】 311 | 312 | ```CSharp 313 | if(order.Details.Count == 0) 314 | { 315 | DoSomething1... 316 | DoSomething2... 317 | DoSomething3... 318 | } 319 | else 320 | continue; 321 | ``` 322 | 323 | 【正例2】 324 | 325 | ```CSharp 326 | if(order.Details.Count > 0) continue; 327 | DoSomething... 328 | DoSomething2... 329 | DoSomething3... 330 | ``` 331 | 332 | * 出于性能考虑,获取数据库连接、事务和try catch操作等操作尽量移至循环体外处理。 333 | 334 | * 事务范围内只存在事务操作,禁止直接将业务逻辑整个包括在业务范围内。 335 | 336 | 说明:使用事务的原则是快进快出,事务代码尽量少,无脑将代码丢事务里不仅会造成事务执行时间超长,而且也会严重降低并发能力。 337 | 338 | 【反例】 339 | 340 | ```CSharp 341 | using(var trans = new Transaction) 342 | { 343 | try 344 | { 345 | dao.Insert; 346 | httpClient.QueryRemoteResult(); 347 | dao.Update; 348 | trans.Commit(); 349 | } 350 | catch 351 | { 352 | trans.RollBack(); 353 | } 354 | } 355 | ``` 356 | 357 | ## 五、注释规范 358 | 359 | * 未完成的逻辑要加上注释//TOTO 360 | 361 | 【正例】 362 | 363 | ```CSharp 364 | //TODO:the submit function has not compelted 365 | doSomething... 366 | ``` 367 | 368 | * 属性以及方法要添加文档注释以提供智能提示,方法参数、泛型以及返回值等也要在文档注释中添加对应的param tag。 369 | 370 | 【正例】 371 | 372 | ```CSharp 373 | /// 374 | /// 更新一个实体 375 | /// 376 | /// 泛型类型 377 | /// 持久化对象 378 | /// 受影响行数 379 | public long Update(T entity) 380 | ``` 381 | 382 | * 如果代码过长,单行注释要独立一行。上下滚动要比左右滚动更适合代码阅读。 383 | 384 | ## 六、其它 385 | 386 | * 禁止使用web.config或者app.config等配置文件进行个性化配置 387 | 388 | 原因:nuget等程序在更新dll版本时会自动编辑项目对应的web.config和app.config,如果将个性化配置也写到里面那么每次发布时都要检查配置文件是否有改动。因此个性化配置应写在自己新建的config文件中,让web.config和app.config可以直接无忧发布。 389 | 390 | * 禁止将本机开发环境配置推送到VCS中。 391 | 392 | * 自定义配置如果有改动,则要发邮件给测试、运维进行测试环境和生产环境的针对性修改。 393 | 394 | * 所有文件保存的操作都必须有按照业务进行定期删除的机制。 395 | 396 | ## 七、命名规范 397 | 398 | * 命名空间以及类名都使用UpperCamelCase 399 | 400 | 【正例】 401 | 402 | ```CSharp 403 | FinanceDomain、OrderDetail 404 | ``` 405 | 406 | * Interface使用IUpperCamerCase 407 | 408 | 【正例】 409 | 410 | ```CSharp 411 | IRespository、ICommand 412 | ``` 413 | 414 | * 方法、属性以及事件使用UpperCamelCase 415 | 416 | 【正例】 417 | 418 | ```CSharp 419 | public User GetUserById(Guid userId) 420 | public List OrderDetails {get;set;} 421 | public event EventHandler Received; 422 | ``` 423 | 424 | * 局部变量使用lowerCamelCase 425 | 426 | 【正例】 427 | 428 | ```CSharp 429 | var user = respository.GetUserById(1); 430 | ``` 431 | 432 | * 局部常量使用lowerCamelCase 433 | 434 | 【正例】 435 | 436 | ```CSharp 437 | const string directoryChar = @"\"; 438 | ``` 439 | 440 | * 参数使用lowerCamelCase 441 | 442 | 【正例】 443 | 444 | ```CSharp 445 | public Order GetOrderById(string orderId) 446 | ``` 447 | 448 | * 非私有字段使用UpperCamelCase 449 | 450 | 【正例】 451 | 452 | ```CSharp 453 | protected RabbitMqProxy RabbitMq = new RabbitMqProxy(); 454 | ``` 455 | 456 | * 私有字段使用_lowerCamelCase 457 | 458 | 【正例】 459 | 460 | ```CSharp 461 | private string _name; 462 | ``` 463 | 464 | * 静态私有字段使用_lowerCamelCase 465 | 466 | 【正例】 467 | 468 | ```CSharp 469 | private static string _connStr; 470 | ``` 471 | 472 | * 非私有常量字段使用UpperCamelCase 473 | 474 | 【正例】 475 | 476 | ```CSharp 477 | public const double KgToOz = 35.2739619; 478 | ``` 479 | 480 | * 私有常量字段使用UpperCamelCase 481 | 482 | 【正例】 483 | 484 | ```CSharp 485 | private const double Pi = 3.1415; 486 | ``` 487 | 488 | * 非私有静态只读字段使用UpperCamelCase 489 | 490 | 【正例】 491 | 492 | ```CSharp 493 | public const string SystemName = "Zaaby"; 494 | ``` 495 | 496 | * 私有静态只读字段使用UpperCamelCase 497 | 498 | 【正例】 499 | 500 | ```CSharp 501 | private static readonly string Table = typeof(T).Name; 502 | ``` 503 | 504 | * 枚举成员使用UpperCamelCase 505 | 506 | 【正例】 507 | 508 | ```CSharp 509 | public enum AppType 510 | { 511 | WebApi = 0, 512 | Mvc = 1, 513 | Rpc = 2 514 | } 515 | ``` 516 | 517 | * 局部函数UpperCamelCase 518 | 519 | 【正例】 520 | 521 | ```CSharp 522 | void GetResult() 523 | { 524 | using (var httpResponse = httpRequest.GetResponse()) 525 | using (var respStream = httpResponse.GetResponseStream()) 526 | using (var reader = new StreamReader(respStream, Encoding.UTF8)) 527 | strResponse = reader.ReadToEnd(); 528 | } 529 | ``` 530 | 531 | * **领域对象成员**禁止使用匈牙利命名法,原因是OOP编程中不需要知道成员基于什么数据结构(Dictionary或者List),使用匈牙利命名法会使得代码更为臃肿。 532 | 533 | 原因:实际上匈牙利命名法源自微软内部一位匈牙利程序员Charles Simonyi,最初被叫做“应用型匈牙利命名法”(Apps Hungarian),因为它是在“应用程序部”(Applications Division)中使用的,例如 Word 和 Excel 。在 Excel 中xl代表“相对于布局的水平坐标”,而xw表示“相对于窗口的横向坐标”,这两者都是同样的整形但明显不能互换。使用匈牙利命名法有助于程序员发现这种编译器无法发现的错误。因此匈牙利命名法中的type并不是我们通常认为的数据结构,但软件界历史原因这已经积重难返导致匈牙利命名法现在变成“系统性匈牙利命名法"。详细参考: 534 | 535 | 【正例】 536 | 537 | ```CSharp 538 | var orderDetails = order.Details; 539 | ``` 540 | 541 | 【反例】 542 | 543 | ```CSharp 544 | var lstDetail = order.LstDetail; 545 | ``` 546 | 547 | * 所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。 548 | 549 | 说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使是纯拼音命名方式也要避免使用。 550 | 551 | * 特性类命名要以Attribute结尾;异常类命名要使用Exception结尾;测试类命名要以它要测试的类的名称开头,以Test结尾;抽象类名要使用Abstract开头。 552 | 553 | * 杜绝完全不规范的缩写,避免望文不知意。以下例子后两者是通过长期耗费开发人员精力才最终约定成俗,但新开发人员接手时依然要培训或者自己看代码内部消化,原因是这些缩写完全没有可读性。 554 | 555 | 【反例】 556 | 557 | ```CSharp 558 | fdId,ctId,ciId(countryId),ptId(postTypeId) 559 | ``` 560 | 561 | * 如果使用了设计模式,建议在类名中体现出具体模式 562 | 563 | 【正例】 564 | 565 | ```CSharp 566 | public class RdbFactory; 567 | public class RabbitMqProxy; 568 | ``` 569 | 570 | * 代表数量的属性使用Quantity而不是Count命名,因为Quantity是名词,而Count含有计算这种动词的含义。 571 | 572 | * 时间类型,精度在日期级别的以Date结尾,精度在时分秒级别的以Time结尾。 573 | 574 | * 使用long类型进行赋值时,要使用大写的L而不能是小写的l,因为小写l容易和数字1混淆造成误解 575 | 576 | 【正例】 577 | 578 | ```CSharp 579 | var snowflakeId = 3L; 580 | ``` 581 | 582 | 【反例】 583 | 584 | ```CSharp 585 | var snowflakeId = 3l; 586 | ``` 587 | 588 | * 禁止使用类名来进行冗余的属性命名。 589 | 590 | 说明:变量名规范,根本不需要根据属性名来判断其属于什么类。另外统一命名为Id还可以便于一些底层架构封装进行统一处理(如以DO为参数类型的仓储类;自动管理新增、修改时间以及数据版本的仓储父类)。 591 | 592 | 【正例】 593 | 594 | ```CSharp 595 | public Guid Id { get; set;} 596 | public string Name { get; set;} 597 | ``` 598 | 599 | 【反例】 600 | 601 | ```CSharp 602 | public Guid UserId { get; set;} 603 | public string UserName { get; set;} 604 | ``` 605 | 606 | * 除了以VO为目标对象,禁止使用如AutoMapper等映射工具。 607 | 608 | * 字段名耦合,要想自动转换必须字段名一致,而各层模型有不同的命名规范,虽然可以通过单一设置来解决,但如果数量多的话和手写hardCode的工作量没多大区别,而且引入了学习映射工具设置的负担。 609 | 610 | * 失去智能提示,如果修改了一个字段名,但在转换设置中没有写,会导致数据无法转换并引发极大的业务风险,而且代码量多的时候要搜索针对某类型是否使用映射代码进行排查也是一个很大的工作量,提高了维护成本。 611 | 612 | * VO只用于展现数据,不会对数据本身造成影响,因此允许使用。 613 | 614 | ## 八、格式规范 615 | 616 | * 所有类成员(属性、方法)之间都要相隔一行空行,方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。 617 | 618 | 说明:可以让成员间的界限更加清晰。 619 | 620 | * 缩进使用制表符,禁止使用空格。(制表符设置为4个空格) 621 | 622 | * 无效的using以及声明/实例禁止存在。 623 | 624 | * 不允许连续多个空行影响代码阅读。 625 | 626 | * 使用var而不是显式地指定变量类型。 627 | 628 | 原因:可以让代码更加简洁,在重构的时候可以减少代码的变动从而减少文件的签出降低冲突以提高可维护性。如果认为使用var导致可读性差,那是因为变量名或者方法名没起好。 629 | 630 | * 方法参数在定义和传入时,多个参数逗号后必须添加空格。 631 | 632 | 【正例】 633 | 634 | ```CSharp 635 | public void SendCommand(string commandName, string body, bool isDeadLetter = false) 636 | SendCommand(exchange, body, isDeadLetter); 637 | ``` 638 | 639 | * 任何运算符左右必须加一个空格 640 | 641 | 说明:运算符包括赋值运算符=、逻辑运算符&&,加减乘除符号以及三目运算符等。 642 | 643 | 【正例】 644 | 645 | ```CSharp 646 | var i = 1; 647 | return order.Details.Any() && order.Completed; 648 | return _virtualHost ?? (_virtualHost = ""); 649 | ``` 650 | 651 | * 除了缩进,禁止连续空格,缩进以四个空格为标准。 652 | 653 | 说明:会让人怀疑你的职业素养…… 654 | 655 | 【反例】 656 | 657 | ```CSharp 658 | var order.TraceId = traceId; 659 | ``` 660 | 661 | * 如果大括号内容为空,则直接写成{}不需要换行;如果非空则分为两种情况: 662 | 663 | * 如果内容只有一行,则不需要大括号,直接相对上一行缩进四个空格来写代码。 664 | 665 | * 如果内容不止一行,则左右花括号独立一行。 666 | --------------------------------------------------------------------------------