《Effective C#》快速笔记(四)

时间:2022-02-11 01:57:43

《Effective C#》快速笔记(四)

  .NET 是一个类库,你了解的越多,自己需要编写的代码就越少。

目录

三十、使用重写而不是事件处理函数

三十一、使用 IComparable<T> 和 IComparer<T> 实现顺序关系

三十二、避免使用 ICloneable 接口

三十三、仅用 new 修饰符处理基类更新

三十四、避免重载基类中定义的方法

三十五、PLINQ 如何实现并行算法

三十六、理解 PLINQ 在 I/O 密集场景

三十七、注意并行算法中的异常

三十、使用重写而不是事件处理函数

  1.处理系统之中触发的事件:要么使用事件处理函数,要么重写基类中的虚方法。在派生类中,你只应该重写虚方法,而事件处理函数则应该使用在对象没有关系的交互中。

  2.从效率角度,重写也比事件处理函数更快,事件处理器需要迭代整个请求列表,这样占用了更多的CPU时间。

  3.但是,事件是在运行时绑定的,因此会带来更好的灵活性。

  4.一个事件处理器抛出异常,则事件链上的其他处理器将不会被调用,而重写的虚方法则不会出现这种情况。

  5.重写只能用于派生类中,其他类型必须使用事件机制。

三十一、使用 IComparable<T> 和 IComparer<T> 实现顺序关系

  1..NET 提供了两个接口 IComparable<T> 和 IComparer<T> 表示顺序关系。IComparable 定义了类型的自然顺序,而 IComparer 则表示描述其他顺序。

  2.IComparable 接口包含一个方法:CompareTo() 。如果当前对象 < 被比较对象,返回值 < 0;当前对象 > 被比较对象,返回值 > 0;两者相等,返回 0。

  3.实现非泛型的 IComparable 接口的原因:保证向后兼容,反射中使用泛型会加大难度。

  4.实现 IComparable 时请使用显示接口实现,并提供一个强类型版本的重载,这个强类型的重载能提高性能,并降低使用者误用 CompareTo 方法的可能。

三十二、避免使用 ICloneable 接口

  1.当对象关系复杂时,深复制会带来不必要的麻烦。

  2.对于内建类型,如整数,深复制和浅复制的结果一样。

  3.內建的值类型不需要支持 ICloneable。赋值语句就可以复制结构中所有的值,且比 Clone() 更高效;而子类,仅在真正需要复制操作时再添加 ICloneable 支持。

  4.对于值类型,永远不要实现 ICloneable,直接使用赋值操作即可。

三十三、仅用 new 修饰符处理基类更新

  1.new 修饰符必须小心谨慎的使用。如果它是有歧意的,就等于在类上创建了个模糊的方法。

  2.只有在特殊情况下才使用,那就是升级基类时与你的类产生冲突时。即使在这种情况下,也应该小心的使用它。最重要的是,其它任何时候都不要用它。

三十四、避免重载基类中定义的方法

  1.为基类中定义的方法创建重载增加了重载解析时的可选项,也就是增加了二义性。很可能你对重载选择的理解和编译期的解析并不相同,从而造成了用户的困惑。解决办法:选择不同的名称,因为这个类是你设计的,自然就可以给出更好,不同的的方法名称。

  2.不要重载那些定义于基类中的方法,这不能带来丝毫意义,只能给使用者平添烦恼,,但不针对重写。

三十五、PLINQ 如何实现并行算法

  1.使用时简单的添加 AsParallel() 即可。

  2.PLNQ 在能够保证正确性的前提下,让程序得到多核环境下的性能提升。

  3.需要理解何时数据访问是必须同步的,也需要衡量 ParallelEnumerable 中并行和顺序版本方法带来的影响。

  4.PLINQ 无法并行化 LINQ to SQL 或 EF 的执行,因为这两样东西会借助数据库引擎来执行并行查询。

  5.每个并行查询都开始于一个分区的操作,PLINQ 需要对输入元素分区,然后指派给负责执行查询的任务。

  6.4 种分区算法:单位分区、区块分区、条带分区和散列分区。

  7.3 种其它算法:管道(Pipelining)、停止并进行(Stop&Go)和反向枚举。

  8.通过在查询开始时添加 AsParallel() 方法,将查询表达式转换成并行执行。

var list = new List<int>(); var query=list.Where(x=>x<150).Select(x=>x.ToString()); //并行查询 var queryParallel = list.AsParallel().Where(x => x < 150).Select(x => x.ToString());

三十六、理解 PLINQ 在 I/O 密集场景

var urls = new List<string>(); foreach (var url in urls) { var result = new WebClient().DownloadData(url); //发出一个同步的 Web 请求,然后等待接收数据,主要会将时间浪费在等待上 Console.WriteLine(result); } //使用并行处理模型 Parallel.ForEach(urls, url => { var result = new WebClient().DownloadData(url); Console.WriteLine(result); }); //使用 PLINQ var results = from url in urls.AsParallel() select new WebClient().DownloadData(url); results.ForAll(Console.Write);

  1.PLINQ 的执行方式和并行任务库的 Parallel.ForEach() 不同。PLINQ 使用固定数目的线程,而 Parallel.ForEach() 会调整线程的数量来增加吞吐量。