编写高质量代码:改善C#程序的157个建议

2014-12-13 推荐: 4 难度: 3

这本书汇集了C#编写中的一些建议、注意事项和小技巧,为了温故知新,又拿出来翻看了一遍。很多主题要阐述的内容是显而易见、不言自明的,就没有再写摘要。部分主题的名称起得并不够好,我进行了改动;个别建议是有争议的,我修改为了我认为合理的方式(红色字体)。

基本语言要素

  1. 正确操作字符串

    注意隐式的装箱和拆箱,string str = "a" + 1.ToString(); 优于 string str = "a" + 1;

    拼装多个字符串时,使用 StringBuilder。

  2. 类型转换的方法
    1. 类型转换运算符(operator重载),例如 float j=0; int i = (int)j;
    2. 类型的静态方法,例如 TryParse()、Parse();实例方法,ToString()、ToDatetime()
    3. 使用帮助类,System.Convert,System.BitConvert
    4. 基类和子类之间的转换。
  3. 区别 as 与 is

    能使用as的情况下使用as,性能会优于is。

    as只能用于引用类型,is则都可以。

  4. 总是使用TryParse,不使用Parse
  5. 恰当使用Nullable<T>类型

    如果数据库的字段可为null,那么对应的C#实体类属性也应当可以为null。使用int?而不是int。

  6. 区别使用readonly和const

    const是编译时常量,readonly是运行时常量;const只能加在基元类型上,且默认为static,readonly则无此限制。

  7. 将0作为枚举项的默认值
  8. 枚举要么全部显式赋值,要么全部不要赋值

    个人建议全部显式赋值,因为很多时候是将枚举项的值存在数据库当中,如果不显示赋值,那么当在枚举项中间插入一个值的时候,后面的值会全部改变。因为枚举值默认是+1累进的。

  9. 为类型添加运算符重载
  10. 可以考虑为类型实现IComparable或者IComparer比较器接口
  11. 区别==和Equals()

    默认情况下,它们是一样的;但通常重载Equals()表示“值相等”,==来表示引用相等。

  12. 重写Equals()时同时重写GetHashCode()

    否则会出现潜在的不易察觉的BUG,例如在Dictionary<Key, Value>中,CLR内部查找的实际是Key值的HashCode。

  13. 类型格式化字符串

    IFormattable和IFormatProvider的用法。这个两个接口项目中不常使用,但是IFormatProvider倒是可以作为分离变化的教程来学习。

  14. 深度复制和浅度复制
  15. 使用dynamic简化反射的实现

    效率可以提高很多。

集合和LINQ

  1. 元素数量固定使用数组;不固定使用List<T>。

    很多算法针对数组有优化。

  2. 遍历时尽可能使用foreach

    1. 语法简化; 2.如果实现了IDispose,则会自动调用。

  3. foreach不能代替for
    1. foreach遍历时不可以对集合进行修改。
    2. foreach使用迭代器进行遍历,for使用索引器。
  4. 使用集合初始化器对集合进行初始化

    数组:string[] list = { "1", "2", "3" };

    列表:List<Item> list = new List<Item>() { new Item(), new Item(), new Item() };

  5. 使用泛型集合,不再使用ArrayList
  6. 选择合适的集合

    常用集合:数组、List<T>、Stack<T>、Queue<T>、Dictionary<TKey, TValue>、LinkedList<T>、HashSet<T>。

  7. 确保集合的线程安全

    当T1对集合读,T2对集合写时,则可能出现潜在的BUG。

  8. 不要用List<T>作为自定义集合类的基类

    因为List<T>没有virtual方法,无法重写,因此最好自己实现一个集合类的基类。

  9. 迭代器应当是只读的
  10. 注意集合类型成员的可访问性

    如果集合类型的属性是可写的,则可能引发线程安全的问题(建议22)。因此最好这样设置:public List<Item> ItemList { get; private set; },只允许类型内部成员对集合属性进行赋值。

  11. 使用匿名类型存储LINQ查询结果
  12. 在查询中使用Lambda表达式

    实际上我只用Lambda表达式,因为个人认为LINQ的查询语法相当怪异,像SQL又有不同,看上去很别扭。

  13. 理解延迟求值和主动求值的区别

    LINQ查询表达式并没有立即执行,只有在执行ToList()或者遍历时才会执行。

  14. 区别LINQ中的IEnumerable<T>和IQuerable<T>

    简单来说,IEnumerable<T>针对的是LINQ to Objects,IQuerable<T>针对的是LINQ to SQL。

  15. 使用LINQ,不需要再使用集合的比较器和迭代器

    LINQ极大简化了集合操作,理解比较器和迭代器的原理还是重要的,但开发中已经不再需要去实现它们了。

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

    使用First()、Take()等方法,比使用Where()迭代的次数更少。

感谢阅读,希望这篇文章能给你带来帮助!