读书笔记之《编写高质量代码:改善C#程序的157个建议》

来源:互联网 发布:php 函数 变量='' 编辑:程序博客网 时间:2024/05/18 22:16

最近,在阅读书籍《编写高质量代码:改善C#程序的157个建议》,感觉写得很不错,特将其中的建议整理了一下,待以后随时查看。

现只罗列了其中的部分建议,因为书籍还没有阅读完,会慢慢的完善补充。

 

正确操作字符串

1.1 确保尽量少的装箱

在使用其他值引用类型到字符串的转换并完成拼接时,应当避免使用操作符“+”来完成,而应该使用值引用类型提供的ToString方法。

例如:

String str1=str1+9

String str2=str2+9.ToString();

1.2避免分配额外的内存空间

 

使用默认转型方法

这些转型方法包括:

使用类型的转换运算符。隐式转换与显式转换。

使用类型内置的ParseTryParse,或者如ToStringToDoubleToDateTime等方法。

使用帮助类提供的方法。如System.ConvertSystem.BitConverter等。

使用CLR支持的转型。上溯转型与下溯转型。

 

区别对待强制转型与asis

如果类型之间都上溯到了某个共同的基类,那么根据此基类进行的转型(即基类转型为子类本身)应该使用as,子类与子类之间的转型,则应该提供转换操作符,以便进行强制转型。

 

4 TryParseParse

两者最大的区别是,如果字符串格式不满足转换的要求,Parse方法将会引发一个异常;TryParse方法将不会引发异常,它会返回false,同时将result置为0

 

使用int?来确保值类型也可以为null

T?是NullableT〉的缩写,两者可以相互转换。T必须为结构体。

 

区别readonlyconst的使用方法

两者的区别:

const是一个编译器常量,readonly是一个运行时常量。

const只能修饰基元类型,枚举类型或字符串类型,readonly没有限制。

 

0值作为枚举的默认值

 

避免给枚举类型的元素提供显式的值

 

习惯重载运算符

 

10 创建对象时需要考虑是否实现比较器

 

11 区别对待== 和 Equals

无论是操作符“==”还是方法“Equals”,都倾向于表达这样一个原则:

对于值类型,如果类型的值相等,就应该返回True

对于引用类型,如果类型指向同一个对象,则返回True

FCL中,string的比较被重载为针对“类型的值”的比较,而不是针对“引用本身”的比较。

 

12 重写Equals时也要重写GetHashCode

 

13 为类型输出格式化字符串

IFormattable接口,IFormatProvider接口,ICustomFormatter接口的使用。

 

14 正确实现浅拷贝和深拷贝

浅拷贝:将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值。而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身。

深拷贝:同样,将对象中的所有字段复制到新的对象(副本)中。不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。

 

15 使用Dynamic来简化反射实现

 

16 元素数量可变的情况下不应使用数组

 

17 多数情况下使用foreach进行循环遍历

foreach优点:语法更简化,默认调用Dispose方法。

 

18 foreach不能代替for

foreach的一个缺点:不支持循环时对集合进行增删操作(因为版本检测的缘故)。

 

19 使用更有效的对象和几何初始化

对象和集合初始值设定项

 

20 使用泛型集合代替非泛型集合

 

21 选择正确的集合

如果集合的数据固定并且不涉及转型,使用数组效率高,否则就使用List<T>。

 

22 确保集合的线程安全

集合线程安全是指在多个线程上添加或者删除元素时,线程之间必须保持同步。

加锁或者使用线程安全的集合类(位于System.Collections.Concurrent命名空间下)。

 

23 避免将List<T>作为自定义集合类的基类

 

24 迭代器应该是只读的

 

25 谨慎集合属性的可写操作

如果类型的属性中有集合属性,那么应该保证属性对象是有类型本身产生的。

 

26 使用匿名类型存储LINQ查询结果

 

27 在查询中使用Lambda表达式

 

28 区别LINQ查询中德IEnumerable<T>与IQueryable<T>

 

30 使用LINQ取代集合中德比较器和迭代器

 

31在LINQ查询中避免不必要的迭代

编码过程中,要充分运用First和Take等方法。

 

32 总是优先考虑泛型

 

33 避免在泛型类型中声明静态成员

泛型类型之间不共享静态成员;非泛型类型中的泛型方法并不会在运行时的本地代码中生成不同的类型。

 

34 为泛型参数设定约束

 

35 使用default为泛型类型变量指定初始值

 

36 使用FCL中的委托类型

 

37 使用Lambda表达式代替方法和匿名方法

 

38 小心闭包中的陷阱

 

39 了解委托的实质

 

40 使用event关键字为委托施加保护

 

41 实现标准的事件模型

 

42 使用泛型参数兼容泛型接口的不可变性

 

43 让接口中的泛型参数支持协变

 

44 理解委托中的协变

 

45 为泛型类型参数指定逆变

 

46 显式释放资源需继承接口IDisposable

 

47 即使提供了显式释放方法,也应该在终结器中提供隐式清理

~XXClass()

{Dispose(false);}

 

48 Dispose方法应允许被多次调用

 

49 在Dispose模式中应提取一个受保护的虚方法

存在派生类时,提醒派生类在自己的释放方法中调用base.Dispose方法。

 

50 在Dispose模式中应区别对待托管资源和非托管资源

 

51 具有可释放字段的类型或拥有本机资源的类型应该是可释放的

 

52 及时释放资源

 

58 涌抛出异常代替返回错误代码

 

59 不要在不恰当的场合下引发异常

正常的业务流程不应使用异常来处理。

不要总是尝试去捕获异常或引发异常,而应该允许异常向调用堆栈往上传播。

应该引发异常的情况:

第一类情况 如果运行代码后会造成内存泄露,资源不可用,或者应用程序状态不可恢复,则引发异常。

第二类情况 在捕获异常的时候,如果需要包装一些更有用的信息,则引发异常。

第三类情况 如果底层异常在高层操作的上下文中没有意义,则可以考虑捕获这些底层异常,并引发新的有意义的异常。

 

60 重新引发异常时使用Inner Exception

 

61 避免在finally内撰写无效代码

 

62 避免嵌套异常

直接throw e而不是throw将会重置堆栈信息。

 

63 避免吃掉异常

如果你不知道如何处理某个异常,那么千万不要“吃掉”异常;如果异常可被预见,并且通常情况它不能算是一个Bug时,可以考虑“吃掉”异常。

 

64 为循环增加Tester-Doer模式而不是将try—catch置于循环内

因为循环中抛出异常是一个相当影响性能的问题。

 

65 总是处理未捕获的异常

 

 

90 不要为抽象类提供公开的构造方法

 

91 可见字段应该重构为属性

 

92 谨慎将数组或集合作为属性

 

93 构造方法应初始化主要属性和字段

 

94 区别对待override和new

 

95 避免在构造方法中调用虚成员

 

96 成员应优先考虑公开将基类型或接口

 

97 优先考虑将基类型或接口作为参数传递

 

98 用params减少重复参数

 

99 重写时不应使用子类参数

 

100 静态方和实例方法没有区别

 

101 使用扩展方法,向现有类型“添加”方法

 

102 

 

122 以<Company>.<Component>为明明空间命名

 

123 程序集不必与命名空间同名

 

124 考虑在命名空间中使用复数

如System.Collections(复数形式),包含所有的非泛型集合类。

 

125 避免用FCL的类型名称命名自己的类型

126 用名词和名词组给类型命名

 

127 用形容词组给接口命名

如IDisposable,IEnumerable等,也有例外,如IEnumerator。

 

128 考虑让派生类的名字以基类名字作为前缀

如Exception类及其派生类。

 

129 泛型类型参数要以T作为前缀

 

130 以复数命名枚举类型,以单数命名枚举元素

 

131 使用PascalCasing命名公开元素

 

132 考虑用类名作为属性名

如果属性对应一个类型,且仅有一个该类型的属性,可以考虑用类名作为属性名。

 

133 camelCasing命名私有字段和局部变量

 

134 有条件的使用前缀

大类型(该类代码足够长)中,使用前缀来区分一个类型是实例变量(m_)还是静态变量(s_)或者是一个const变量(名词大写加下划线)。前缀仅限于此。

 

135 考虑使用肯定性的短语命名布尔属性

IsXXHasXXCanXXAllowXX等。

 

136 优先使用后缀表示已有类型的新版本

X509CertificateX509Certificate2(替代版本)。

 

137 委托和事件类型应该添加上级后缀(参考建议128

如加上DelegateCallBack(回调),EventHandler

 

138 事件和委托变量使用动词或形容词短语命名

 

139 事件处理器命名采用组合方式

事件处理器即实际被委托执行的那个方法。

事件处理器的命名规则:事件变量所属对象+下划线+事件变量名

为委托或委托中的回调编写处理器:委托变量所属对象+On+委托变量名

 

140 使用默认的访问修饰符

 

141 不知道该不该用大括号时,就用

 

142 总是提供有意义的命名

143 方法抽象级别应在同一层次

 

144 一个方法只做一件事

 

145 避免过长的方法和过长的类

 

146 只对外公布必要的操作

 

147 重构多个相关属性为一个类

类型中的相关属性超过3个,就可以考虑将其重构为一个类。

 

148 不重复代码

保持代码的简洁,避免Bug的出现。

 

149 使用表驱动法避免过长的ifswitch分支

例如按照索引值驱动的表驱动法。

 

150 使用匿名方法,Lambda表达式代替方法

 

151 使用事件访问器替换公开的事件成员变量

类似于属性之于公有字段。

 

152 最少,甚至是不要注释

 

153 若抛出异常,则必须要注释

 

154 不要过度设计,在敏捷中体会重构的乐趣

说实话,没怎么看,对敏捷不太感兴趣。

 

155 随生产代码一起提交单元测试代码

 

156 利用特性为应用程序提供多个版本

 

157 从写第一个界面开始,就进行自动化测试

 

 

原创粉丝点击