C#本质论第一章。
语言规范
- 要更注重标识符的清晰而不是简短。
- 不要在标识符名称中使用单词缩写。
- 不要使用不被广泛接受的首字母缩写词,即使被广泛接受,非必要时也不要用。
- 要把只含两个字母的首字母缩写词全部大写,除非他是驼峰大小写风格标识符的第一个单词。
- 包含三个或者更多字母的首字母缩写词,仅第一个字母需要大写,除非该缩写词是驼峰大小写风格标识符的第一个单词。
- 在驼峰大小写风格标识符开头的首字母缩写词中,所有字母都不要大写。
- 不要为变量名称添加类型前缀。
- 要用名词或者名词短语命名类。
- 要为所有类名使用
Pascal
大小写风格。 - 要为局部变量使用
camel
大小写风格的命名。 - 不要使用注释,除非代码本身一言难尽。
- 要尽量编写清晰的代码,而不是通过注释澄清复杂的代码。
- 要在指定数据类型是使用C#关键字而不是BCL名称。
- 要保持一致而不是变来变去。
- 要使用大写的字面量后缀。
- 要使用圆括号增加代码的易读性,尤其是在操作符优先级不是让人一目了然的时候。
- 当必须进行本地化时,要用组合格式化而不是加法操作符来拼接字符串。
- 避免在需要准确的十进制算术运算时使用二进制浮点类型,而是使用decimal浮点类型。
- 避免将二进制浮点类型用于相等性条件式,要么判断两个值之差是否在容差范围内,要么使用decimal类型。
- 避免混淆递增和递减操作符的用法。
- 不要用常量表示任何将来可能改变的任何值。
- 避免在
if
语句中省略大括号,除非只有一行语句。 - 考虑使用
if/else
语句,而不是使用过于复杂的条件表达式。 - 如果发现正在写的
for
循环包含了多个复杂条件和多个循环变量,要考虑重构方法,以使控制流更容易理解。 - 假如事先知道循环次数,而且循环中需要用到控制循环次数的计数器,那么要使用
for
循环,如果事先不知道循环次数且不需要计数器,那么要用while
循环。 - 不要使用
continue
作为跳转语句退出switch
语句,如果switch
语句在一个循环中使用,这样写是合法的,但是这样做很容易对之后出现的break
的含义感到迷惑。 - 避免使用
goto
。 - 要为方法名使用动词或动词短语。
- 要为命名空间使用
Pascal
大小写风格。 - 考虑将源代码的文件目录结构组织成与命名空间的层次结构相匹配的形式。
- 要为参数名使用驼峰大小写风格。
- 当一个方法需要处理任意数量(包括零个)额外实参时,要使用参数数组。
- 要尽量为所有参数提供好的默认值
- 要提供简单的方法重载,其必需的参数的数量要少。
- 考虑从最简单到最复杂来组织重载。
- 要将参数名视为API的一部分。如果API之间的版本兼容性很重要,就要避免更改参数名。
- 避免从
finally
块显式的引发异常。 - 要优先使用
try/finally
而不是try/catch
块来实现资源清理代码。 - 要在抛出的异常中描述异常为什么发生,如有可能,还要说明如何防范。
- 避免使用常规catch块,而应该使用捕获
System.Exception
的catch
块来代替。 - 避免捕获无法获知其正确行动的异常,对这种异常不进行处理比处理的不正确要好。
- 避免在重新引发前捕获和记录异常,要允许异常逃脱,直至它被正确处理。
- 要在捕获并重新引发异常时使用空的throw语句,以便保持调用栈。
- 要通过引发异常而不是返回错误码来报告执行失败。
- 不要让公共成员将异常作为返回值或者
out
参数。要通过异常来指明错误;不要通过它们作为返回值来指明错误。 - 不要用异常来处理正常的、预期的情况;用异常处理异常的、非预期的情况。
- 不要在一个源代码文件中放置多个类。
- 要用所含公共类型的名称来命名源代码文件。
- 要使用属性简化对简单数据的访问。
- 避免从属性的取值方法中引发异常。
- 要在属性引发异常时保留原始属性值。
- 如果没有额外的实现逻辑,要优先使用自动实现的属性,而不是带有简单支持字段的属性。
- 考虑为支持字段和属性使用相同的大小写风格,为支持字段添加”_”前缀,但不要使用双下划线,因为以双下划线开头的标识符是为C#编译器保留的。
- 要使用名词,名词短语或形容词来命名属性。
- 考虑让属性和它的类型同名。
- 避免用驼峰大小写风格命名字段。
- 如果有用的话,要为布尔属性附加“Is”“Can”或“Has”前缀。
- 不要声明public或protected的实例字段,而是通过属性来公开字段。
- 要用Pascal大小写风格命名属性。
- 要优先使用自动实现的属性而不是字段。
- 如果没有额外的实现逻辑,要优先使用自动实现的属性,而不是自己编写完整版本。
- 避免从属性外部访问属性的支持字段。
- 调用ArgumentException()或ArgumentNullException()构造器时,要为pramaName参数传递“Value”。
- 如果不想调用者更改属性的值,要创建只读属性。
- 要为所有属性的取值方法和赋值方法的实现应用适当的可访问性修饰符。
- 不要提供只写属性,也不要让属性的赋值方法的可访问性比取值方法更宽松。
- 要为所有属性提供有意义的默认值,确保默认值不会造成安全漏洞或者造成代码效率大幅下降。对于自动实现的属性,要通过构造器设置默认值。
- 要允许以任意顺序设置属性,即使这会造成对象临时处于无效状态。
- 如果使用构造器参数来设置属性,构造器参数要使用和属性相同的名称,区别仅仅是大小写风格。
- 要为构造器提供可选参数,或者提供便利的重载构造器,用有意义的默认值初始化属性。
- 考虑以内联方式初始化静态字段,不要使用静态构造器或者在声明时赋值。
- 避免轻率的定义拓展方法,尤其是要避免为自己没有所有权的类型定义拓展方法。
- 要为永远不变的值使用常量字段。
- 不要为将来会变化的值使用常量字段。
- 在C#6.0之后,要优先使用只读的自动实现的属性,而不是定义只读字段。
- 在C#6.0之后,要为预定义对象实例使用public static readonly 字段。
- 如果API的兼容性有要求,要避免将readonly字段修改为只读的自动实现的属性。
- 避免声明公共嵌套类型,唯一的例外是在这种类型的声明没有多大意义的时候,或者这种类型的声明是与一种高级的自定义场景有关。
- 接口名称要使用Pascal大小写风格,并以“I”作为前缀。
- 避免显式实现接口成员,除非有很好的理由。但如果不确定成员的用途,就先选择显式实现。
- 考虑通过定义接口来获得和多继承相似的效果。
- 不要为已交付的接口添加成员。
- 一般要优先选择类而不是接口,用抽象类将契约与实现细节分离开。想在已从其他类型派生的类型上支持接口所定义的功能时,就考虑定义接口。
- 避免使用无成员的标记接口,而是使用特性。
- 不要创建消耗内存大于16字节的值类型。
- 要创建不可变的值类型。
- 要确保结构的默认值有效,总是可以获得结构默认的“全零”值。
Main方法的声明
args
参数是一个字符串数组,用于接受命令行参数,但此数组的第一个元素不是程序名称,而是可执行文件名称之后的第一个命令行参数。可用System.Environment.CommandLine
来获取执行程序
Main
返回的int
值是状态码,返回非零值通常意味着错误。
格式字符串
System.Console.WriteLine(
"Your full name is {0} {1}.",firstName,lastName);
两个索引占位符,每一个占位符对应着格式字符串之后顺序出现的参数。索引值从零开始,每个要插入的参数(格式项)按照对应的顺序排列在格式字符串之后。
注释
/*注释*/
带分隔符的注释
.//注释
单行注释/**注释**/
XML带分隔符的注释///注释
XML单行注释
应用程序接口
应用程序接口(Application Programming Interface,API)定义了软件程序如何与部件交互,是一组数据类型的所有API的结合构建了这组部件集合的API。
对于程序集的组合,每个程序集的API组合在一起成为一个更大的API,一般称为框架。
API包括一系列接口和协议,它们定义了程序和一组部件交互的规则。
托管执行与公共语言基础结构
C#编译器将C#源代码文件转换成中间语言(CIL),然后通过虚拟执行系统将CIL代码即时编译为机器码。