《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() 会调整线程的数量来增加吞吐量。

  2.那些混合了 I/O 密集和 CPU 密集的操作来说,Parallel.ForEach() 更适合。Parallel.ForEach() 会根据当前的负载动态调整线程数量。当很多线程因为等待 I/O 操作而阻塞时,Parallel.ForEach() 会创建更多的线程提高吞吐量。当很多线程都在工作时,Parallel.ForEach() 也会限制活动线程的数量,降低上下文切换的代价。

  3.对于那些需要访问其他计算机,并等待远程响应的程序来说,并行任务库和 PLINQ 起到很重要的作用。

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

  1.后台线程中发生的异常会在不同的方面增加复杂度。异常不能穿过线程边界保留调用栈,当异常传递到开始线程的方法时,线程就会中止。调用线程无法捕获这个错误,也就不能进行对应的处理。

  2.一旦后台线程抛出异常,其它的后台操作也会停止。最好是不要在并行算法中抛出异常。不过其它意料之外的异常也可能会出现。

本系列

  《Effective C#》快速笔记(一)- C# 语言习惯

  《Effective C#》快速笔记(二)- .NET 资源托管

  《Effective C#》快速笔记(三)- 使用 C# 表达设计

  《Effective C#》快速笔记(四) - 使用框架

  《Effective C#》快速笔记(五) - C# 中的动态编程

  《Effective C#》快速笔记(六) - C# 高效编程要点补充

时间: 2024-12-08 18:43:14

《Effective C#》快速笔记(四)- 使用框架的相关文章

openstack学习笔记四 组件框架

组件 nova    对虚拟机管理 cinder  云硬盘 swift     对象存储 容器 glance   镜像 neutron  网络 kestone   验证       所有的组件都得要向keystone进行注册,登记 利用 rebitmq 互相通信 环境需要mysql 框架 控制节点      keystone      dashboard 计算节点      跑虚拟机的机器 网络节点      为计算节点里的虚拟机提供路由  DHCP 存储节点 [[email protected

[.NET] 《Effective C#》快速笔记 - C# 高效编程要点补充

<Effective C#>快速笔记 - C# 高效编程要点补充 目录 四十五.尽量减少装箱拆箱 四十六.为应用程序创建专门的异常类 四十七.使用强异常安全保证 四十八.尽量使用安全的代码 四十九.实现与 CLS 兼容的程序集 五十.实现小尺寸.高内聚的程序集 这是这一系列的最后一篇. 四十五.尽量减少装箱拆箱 值类型是数据的容器,不支持多态. 装箱把一个值类型放在一个未确定类型的引用对象中,让该值作为引用类型所使用.拆箱指从引用类型的位置取出值的一个副本. 装箱拆箱都是比较影响性能的手段,应

《Effective C#》快速笔记(二)- .NET 资源托管

简介 续 <Effective C#>读书笔记(一)- C# 语言习惯. .NET 中,GC 会帮助我们管理内存,我们并不需要去担心内存泄漏,资源分配和指针初始化等问题.不过,它也并非万能,因为非托管资源需要我们自己进行清理,如文件句柄.数据库连接.GDI+ 对象和COM 对象等. 目录 十二.推荐使用成员初始化器而不是赋值语句 十三.正确地初始化静态成员变量 十四.尽量减少重复的初始化逻辑 十五.使用 using 和 try/finally 清理资源 十六.避免创建非必要的对象 十七.实现标

《Effective C#》快速笔记 - C# 中的动态编程

静态类型和动态类型各有所长,静态类型能够让编译器帮你找出更多的错误,因为编译器能够在编译时进行大部分的检查工作.C# 是一种静态类型的语言,不过它加入了动态类型的语言特性,可以更高效地解决问题. 一.目录 三十八.理解动态类型的优劣 三十九.使用动态类型表达泛型类型参数的运行时类型 四十.将接受匿名类型的参数声明为 dynamic 四十一.用 DynamicObject 或 IDynamicMetaObjectProvider 实现数据驱动的动态类型 四十二.如何使用表达式 API 四十三.使用

《Effective C#》快速笔记 - C# 高效编程要点补充

目录 四十五.尽量减少装箱拆箱 四十六.为应用程序创建专门的异常类 四十七.使用强异常安全保证 四十八.尽量使用安全的代码 四十九.实现与 CLS 兼容的程序集 五十.实现小尺寸.高内聚的程序集 这是该系列的最后一篇.也许有些理论有可能会过时,我想它仍有存在的必要,人的知识水平也是一个不断成长的过程,学会站在前人的肩膀上,尝试不断的借鉴与总结. 四十五.尽量减少装箱拆箱 值类型是数据的容器,不支持多态. 装箱把一个值类型放在一个未确定类型的引用对象中,让该值作为引用类型所使用.拆箱指从引用类型的

《Effective C#》快速笔记(一)- C# 语言习惯

目录 一.使用属性而不是可访问的数据成员 二.使用运行时常量(readonly)而不是编译时常量(const) 三.推荐使用 is 或 as 操作符而不是强制类型转换 四.使用 Conditional 特性而不是 #if 条件编译 五.为类型提供 ToString() 方法 六.理解几个等同性判断之间的关系 七.理解 GetHashCode() 的陷阱 八.推荐使用查询语法而不是循环 九.避免在 API 中使用转换操作符 十.使用可选参数减少方法重载的数量 十一.理解短小方法的优势 一.使用属性

[.NET] 《Effective C#》快速笔记 - C# 中的动态编程(初稿)

<Effective C#>快速笔记 - C# 中的动态编程 静态类型和动态类型各有所长,静态类型能够让编译器帮你找出更多的错误,因为编译器能够在编译时进行大部分的检查工作.C# 是一种静态类型的语言,不过它加入了动态类型的语言特性,可以更高效地解决问题. 本系列 <Effective C#>快速笔记(一)- C# 语言习惯 <Effective C#>快速笔记(二)- .NET 资源托管 <Effective C#>快速笔记(三)- 使用 C# 表达设计

《Effective C#》快速笔记(三)- 使用 C# 表达设计

<Effective C#>快速笔记(三)- 使用 C# 表达设计 目录 二十一.限制类型的可见性 二十二.通过定义并实现接口替代继续 二十三.理解接口方法和虚方法的区别 二十四.用委托实现回调 二十五.用事件模式实现通知 二十六.避免返回对内部类对象的引用 二十七.让类型支持序列化 二十八.提供组粒度的因特网服务 API 二十九.支持泛型协变和逆变 二十一.限制类型的可见性 1.在保证类型可以完成工作的前提下,应该尽可能地给类型分配最小的可见性. 2.我们经常下意识的创建公有类型.可见性越低

Effective Objective-C 读书笔记

一本不错的书,给出了52条建议来优化程序的性能,对初学者有不错的指导作用,但是对高级阶段的程序员可能帮助不是很大.这里贴出部分笔记: 第2条: 使用#improt导入头文件会把头文件的内容全部暴露到目标文件中,而且如果两个类之间存在循环引用则会出现编译错误,所以要尽量使用@class进行类声明. 如果需要实现一个协议,则必须#improt这个协议的头文件,所以可以将协议单独定义在一个.h文件当中.如果这个协议是代理模式协议的一部分,即需要与类捆绑使用才有实际意义,则建议定义在类当中,并以类名为前