【进阶修炼】——改善C#程序质量(2)
2014-10-09 16:28 xiashengwang 阅读(405) 评论(0) 编辑 收藏 举报16, 元素可变的情况下应避免用数组。
数组是定长的集合,可以考虑用ArrayList或List<T>集合。ArrayList元素是object类型,有装箱的开销,性能较低。另外Array类提供了Array.CreateInstance来创建数组,Array.Copy来拷贝数组,但这牵涉到新数组的创建,会增加开销。
17, 多数情况下用foreach代替for循环。
18, Foreach不能代替for。
Foreach不能对元素进行增删操作,不能访问序号。
19, 使用对象初始化器和集合初始化器。
这是在.net 3.5以后增加的语法,在Linq查询结果的匿名类上只能使用对象初始化器赋值。
20, 使用泛型集合替代非泛型集合。
类似于ArrayList这种非泛型集合,没有类型安全性检查,并伴随有装箱、拆箱等性能损耗。
21, 选择合适的集合。
List<T>平时用的最多,它追加数据比较快,但是插入和删除数据比较慢,因为插入点以后的数据要全部移动。LinkedList,这是双向链表,插入和删除数据都较快,它没有Add方法,只有AddAfter,AddFirst等方法。Dictionary<K,V>这是一个基于散列值的字典类,查询速度较快,不允许有重复的键值。HashSet<T>,这是集在.net中的实现,存储无序的不重复的元素,如你重复两次添加2进去,最后一次没有效果,但不会出错。根据前面几个基本类型,可以衍生出可以排序的SortedList,SortedDictionary,SortedSet。另外在多线程环境中使用的ConcurrentBag(对应List),ConcurrentDictionary,ConcurrentQueue,ConcurrentStack。
22, 确保集合的线程安全。
多线程访问同一集合,应考虑使用lock关键字对使用集合的代码进行锁定。或可以考虑上面提到的在多线程环境下使用的集合。
23, 避免将List<T>作为自定义集合类的基类。
应该以组合的方式将List<T>包含在自定义类中,然后扩展相应的泛型接口IEnumerable<T>和ICollection<T>(或ICollection<T>的子接口如IList<T>),前者规范了迭代行为,后者规范了集合的操作。原因是List<T>几乎没有提供可供子类复写的方法。如果要向Add方法中加入自己的逻辑,只能覆盖(new关键字)这个方法,而使用者并不知道我们改变了这个意图,他认为自定义类还是一个List<T>,结果就是添加的结果和他预想的不一致。
24, 迭代器应该是只读的。
集合的迭代行为应该是固定的,不能被随意改变。
25, 谨慎集合属性的可写操作。
集合属性的创建,应该由对象内部完成,外界只能访问这个集合,而不应该允许设置成其他的集合。可以对属性使用private set来控制。
26, 使用匿名类型存储Linq查询的结果。
匿名类型可以临时存储数据,避免代码膨胀,过度设计大量的自定义类。匿名类型支持智能感知,匿名类型和Linq搭配使用大有用途。
27, 在查询中使用Lambda表达式。
Linq本质上是使用扩展方法和Lambda表达式来实现的。Linq的大部分扩展方法都是针对Action,Func,Predicate这几个泛型委托的,而Lambda其实就是一个简洁的委托,它可以简化代码。=> 左边是函数的参数,右边是函数体。
28, 理解延迟求值和主动求职的区别。
Linq表达式是一种延迟求值,只有使用时才会正真的计算值,如foreach时,或者调用ToList()方法时。理解这一点很重要,可以避免一些不必要的计算开销。
29, 区别Linq查询中的IEnumerable<T>和IQueryable<T>。
Linq查询方法提供了两类扩展方法,一类放在System.Linq.Enumerable类中,一类放在System.Linq.Queryable中。Enumerable中的扩展方法是对本地集合进行查询,查询的参数是Func<>,适合于Linq to Objects。Queryable中的扩展方法参数是Expression<Func<>>表达式树,这是针对Linq to SQL的,表达式树会被翻译成对应的SQL文。
30, 使用Linq取代集合中的比较器和迭代器。
Linq的orderby等语法实质是调用了IEnumerable的扩展方法来进行排序,这比让类型实现IComparable接口或自定义ICompare接口有更好的扩展性,并且代码更加简洁。
31, 避免不必要的迭代。
这一点说明的是Linq查询的条件应更加具体,如我们只需要第一条数据,可以加一个First()方法,只需要前5条数据,可以加一个Take(5)方法。当满足我们设置的这些条件时,结果就会返回,从而避免不必要的后续迭代。