随笔分类 - 编写高质量代码改善C#程序的157个建议
摘要:建议35:使用default为泛型类型变量指定初始值 有些算法,比如泛型集合List<T>的Find算法,所查找的对象可能会是值类型,也有可能是引用类型。在这种算法内部,我们常常会为这些值类型或引用类型变量指定默认值。于是,问题来了:值类型变量的默认初始值是0值,而引用类型变量的默认初始值是null
阅读全文
摘要:建议34:为泛型参数设定约束 “约束”这个词可能会引起歧义,有些人肯能认为对泛型参数设定约束是限制参数的使用,实际情况正好相反。没有“约束”的泛型参数作用很有限,倒是“约束”让泛型参数具有了更多的行为和属性。 查看下面代码,我们会发现参数t1或参数t2仅仅具有object的属性和行为,所以几乎不能再
阅读全文
摘要:建议33:避免在泛型类型中声明静态成员 在上一建议中,已经理解了应该将MyList<int>和MyList<string>视作两个完全不同的类型,所以,不应该将MyList<T>中的静态成员理解成MyList<int>和MyList<string>共有的成员。 输入: 2 若果换成泛型: 输出为:
阅读全文
摘要:建议32:总是优先考虑泛型 泛型的优点是多方面的,无论泛型类还是泛型方法都同时具备可重用性、类型安全性和高效率等特性,这是非泛型和非泛型方法无法具备的。 以可重用性为例: 该类型只支持整型,如果要让类型支持字符串,有一种方法是重新设计一个类。但是这两个类型的属性和方法都是非常接近的,如果有一种方法可
阅读全文
摘要:建议31:在LINQ查询中避免不必要的迭代 无论是SQL查询还是LINQ查询,搜索到结果立刻返回总比搜索完所有的结果再将结果返回的效率要高。 示例代码: 针对上述集合,返回年龄等于20的第一个元素。下面有两个查询模式,我们来考虑哪一个效率更高。 通常我们会认为第一种的效率会更高一些,因为它似乎返回的
阅读全文
摘要:建议30:使用LINQ取代集合中的比较器和迭代器 LINQ提供了类似于SQL的语法来实现遍历、筛选与投影集合的功能。 foreach实际隐含调用的是集合对象orderByBaseSalary和orderByBouns的迭代器。以往,如果我们要绕开集合的Sort方法对集合按照一定的顺序进行迭代,则需要
阅读全文
摘要:建议29:区别LINQ查询中的IEnumerable<T>和IQueryable<T> LINQ查询一共提供了两类扩展方法,在System.Linq命名空间下,有两个静态类:Enumerable类,它针对继承了IEnumerable<T>接口的集合进行扩展;Queryable类,它针对继承了IQue
阅读全文
摘要:建议28:理解延迟求值和主动求值之间的区别 要理解延迟求值(lazy evaluation)和主动求值(eager evaluation),先看个例子: 输出: temp1: 11 6 7 8 9temp2: 6 7 8 9 在延迟求职的情况下,只是定义了一个查询,而不是立刻执行。对查询结果的访问每
阅读全文
摘要:建议27:在查询中使用Lambda表达式 LINQ实际上是基于扩展方法和Lambda表达式的。任何LINQ查询都能通过扩展方法的方式来代替。 等价于: 针对LINQ设计的扩展方法大多应用了泛型委托。System命名空间定义了泛型委托Action、Func和Predicate。Action用于执行一个
阅读全文
摘要:建议26:使用匿名类型存储LINQ查询结果 从.NET3.0开始,C#开始支持一个新特性:匿名类型。匿名类型有var、赋值运算符和一个非空初始值(或以new开头的初始化项)组成。匿名类型有如下基本特性: 即支持简单类型也指出复杂类型。简单类型必须是一个非空初始值,复杂类型则是一个以new开头的初始化
阅读全文
摘要:建议25:谨慎集合属性的可写操作 如果类型的属性中有集合属性,那么应该保证属性对象是由类型本身产生的。如果将属性设置为可写,则会增加抛出异常的几率。一般情况下,如果集合属性没有值,则它返回的Count等于0,而不是集合属性的值为null。下面的代码将产生一个NullReferenceExceptio
阅读全文
摘要:建议23:避免将List<T>作为自定义集合类的基类 如果要实现一个自定义的集合类,不应该以一个FCL集合类为基类,反而应扩展相应的泛型接口。FCL结合类应该以组合的形式包含至自定义的集合类,需要扩展的泛型接口通常是IEnumerable<T>和ICollection<T>(或ICollection
阅读全文
摘要:建议24:迭代器应该是只读的 如果注意观察会发现,FCL中的迭代器只有GetEnumerator方法,没有SetEnumerator方法,所有的集合类也没有一个可以写的迭代器属性。原因有二: 一:这违背了设计模式的开闭原则。被设置到集合中的迭代器可能会直接导致集合的行为发生异常或变动。一旦确实需要新
阅读全文
摘要:建议22:确保集合的线程安全 集合线程安全是指多个线程上添加或删除元素时,线程键必须保持同步。 下面代码模拟了一个线程在迭代过程中,另一个线程对元素进行了删除。 以上代码运行过程会抛出InvalidOperationException:“集合已修改,可能无法执行枚举。” 早在泛型集合出现之前,非泛型
阅读全文
摘要:建议21:选择正确的集合 要选择正确的集合,首先要了解一些数据结构的知识。所谓数据结构,就是相互之间存在一种或多种特定关系的数据元素的集合。 集合的分类参考下图: 由于非泛型集合存在效率低及非类型安全的缺点,所以这里只讨论泛型集合。 如果集合的数目固定并且不涉及转型,使用数组效率高,否则使用List
阅读全文
摘要:建议20:使用泛型集合代替非泛型集合 在建议1中我们知道,如果要让代码高效运行,应该尽量避免装箱和拆箱,以及尽量减少转型。很遗憾,在微软提供给我们的第一代集合类型中没有做到这一点,下面我们看ArrayList这个类的使用情况: 上面这段代码充分演示了我们可以将程序写得多么糟糕。首先,ArrayLis
阅读全文
摘要:建议19:使用更有效的对象和集合初始化 依赖于属性和FCL 3.5之后的语法规则,现在我们有了更加简洁有效的对象和集合初始化机制:对象和集合初始化设定项。 对象初始化: 对象初始化设定项支持在大括号中对自动实现的属性进行赋值。以往只能依靠构造方法传值进去,或者在对象构造完毕后对属性进行赋值。现在这些
阅读全文
摘要:建议17:多数情况下使用foreach进行循环遍历 由于本建议涉及集合的遍历,所以在开始讲解本建议之前,我们不妨来设想一下如何对结合进行遍历。假设存在一个数组,其遍历模式可以采用依据索引来进行遍历的方法;又假设存在一个HashTable,其遍历模式可能是按照键值来进行遍历。无论是哪个集合,如果他们的
阅读全文
摘要:建议18:foreach不能代替for 上一个建议中提到了foreach的两个优点:语法更简单,默认调用Dispose方法,所有我们强烈建议在实际的代码编写中更多的使用foreach。但是,该建议也有不适合的场景。 foreach存在一个问题:它不支持循环时对集合进行增删操作。比如,运行下面代码会抛
阅读全文
摘要:建议16:元素数量可变的情况下不应使用数组 在C#中,数组一旦被创建,长度就不能改变。如果我们需要一个动态且可变长度的集合,就应该使用ArrayList或List<T>来创建。 而数组本身,尤其是一维数组,在遇到要求高效率的算法时,则会专门被优化以提升其效率。一维数组也成为向量,其性能是最佳的,在I
阅读全文