随笔分类 - 编写高质量代码改善C#程序的157个建议
摘要:建议55:利用定制特性减少可序列化的字段 特性(attribute)可以声明式地为代码中的目标元素添加注释。运行时可以通过查询这些托管块中的元数据信息,达到改变目标元素运行时行为的目的。System.Runtime.Serialization命名空间下,有4个这样的特性: OnDeserialize
阅读全文
摘要:建议54:为无用字段标注不可序列化 序列化是指这样一种技术:把对象转变成流。相反过程,我们称为反序列化。在很多场合都需要用到这项技术。 把对象保存到本地,在下次运行程序的时候,恢复这个对象。 把对象传到网络中的另外一台终端上,然后在此终端还原这个对象。 其他场合,如:把对象赋值到系统的粘贴板中,然后
阅读全文
摘要:建议53:必要时应将不再使用的对象引用赋值为null 在CLR托管的应用程序中,存在一个“根”的概念,类型的静态字段、方法参数、以及局部变量都可以作为“根”的存在(值类型不能作为“根”,只有引用类型的指针才能作为“根”)。 局部变量在代码运行过程中会在内存中创建一个“根”。在一次垃圾回收中,垃圾回收
阅读全文
摘要:建议52:及时释放资源 垃圾回收机制自动为我们隐式地回收了资源(垃圾回收器会自动调用终结器),那我们为什么要主动释放资源呢? 这是一个WinForm窗体程序的例子,在这个示例中,单击一个按钮负责打开一个文件,单击另一个按钮负责回收说有“代”(代的概念会在下文详细指出)的垃圾。如果连续两次单击打开文件
阅读全文
摘要:建议51:具有可释放字段的类型或拥有本机资源的类型应该是可释放的 在建议50中,我们将C#中的类型分为:普通类型和继承了IDisposable接口的非普通类型。非普通类型除了包含那些托管资源的类型外,本身还包含一个非普通类型的字段。 在标准的Dispose模式中,我们对非普通类型举了一个例子:一个非
阅读全文
摘要:建议50:在Dispose模式中应区别对待托管资源和非托管资源 真正资源释放代码的那个虚方法是带一个bool参数的,带这个参数,是因为我们在资源释放时要区别对待托管资源和非托管资源。 提供给调用者调用的显式释放资源的无参Dispose方法中,调用参数是true: 这表明,这时候代码要同时处理托管资源
阅读全文
摘要:建议49:在Dispose模式中应提取一个受保护的虚方法 在标准的Dispose模式中,真正的IDisposable接口的Dispose方法并没有做实际的清理工作,它其实是调用了下面的这个带bool参数且受保护的的虚方法: 之所以提供这样一个受保护的虚方法,是因为考虑了这个类型会被其他类型继承的情况
阅读全文
摘要:建议48:Dispose方法应允许被多次调用 一个类型的Dispose方法应该允许被多次调用而不抛出异常。鉴于此,类型内部维护了一个私有的bool变量disposed,如下: 在实际清理代码的方法中,加入一下判断: 这意味着,如果类型已经被清理过,那么清理工作将不再进行。 对象被调用过Dispose
阅读全文
摘要:建议47:即使提供了显式释放方法,也应该在终结器中提供隐式清理 在标准的Dispose模式中,我们注意到一个以~开头的方法,如下: 这个方法叫做类型的终结器。提供类型终结器的意义在于,我们不能奢望类型的调用者肯定会主动调用Dispose方法,基于终结器会被垃圾回收这个特点,它被用作资源释放的补救措施
阅读全文
摘要:建议46:显式释放资源需继承接口IDisposable C#中的每一个类型都代表一种资源,资源分为两类: 托管资源:由CLR管理分配和释放的资源,即从CLR里new出来的对象。 非托管资源:不受CLR管理的对象,如Windows内核对象,或者文件、数据库连接、套接字、COOM对象等。 如果我们的类型
阅读全文
摘要:建议45:为泛型类型参数指定逆变 逆变是指方法的参数可以是委托或者泛型接口的参数类型的基类。FCL4.0中支持逆变的常用委托有: Func<int T,out TResult> Predicate<in T> 常用委托有: IComparer<in T> 下面例子演示了泛型类型参数指定逆变所带来的好
阅读全文
摘要:建议44:理解委托中的协变 委托中的泛型变量天然是部分支持协变的。为什么是“部分支持协变”?看下面示例: 上中的GetAManager返回的是一个Manager,但是在使用中,其实是将其赋值给一个泛型参数为Employee的委托变量。因为存在下面一种情况,所以编译不过: 要让上面的代码编译通过,同样
阅读全文
摘要:建议43:让接口中的泛型参数支持协变 除了上一建议中提到的使用泛型参数兼容接口不可变性外,还有一种办法是为接口中的泛型声明加上out关键字来支持协变,如下所示: 这段代码在FCL4.0以前是不能编译通过的,因为IEnumerable<T>这个接口在FCL中没有被声明为IEnumerable<out
阅读全文
摘要:建议42:使用泛型参数兼容泛型接口的不可变性 让返回值类型返回比声明的类型派生程度更大的类型,就是“协变”。如: Programmer是Employee的子类,所以Programmer对象也是Employee对象。方法GetAEmployee返回一个Programmer的对象,也就是相当于返回一个E
阅读全文
摘要:建议41:实现标准的事件模型 上一建议中,我们实现了一个带事件通知的文件传输类FileUploader。虽然已经满足需求,但却不符合C#的编码规范,查看EventHandler的原型声明: 我们应该知道微软为事件模型定义的几个规范: 委托类型的名称已EventHandler结束; 委托原型返回值为v
阅读全文
摘要:建议40:使用event关键字为委托施加保护 在建议中我们实现了一个具有通知功能的文件传输类,如下: 像这样调用: 以上调用者代码本身是和FileUploader类一起的,这起码存在两个问题: 1)如果在Main中另起一个线程,该工作线程则可以将FileProgress委托链置为空: 2)可以在外部
阅读全文
摘要:建议39:了解委托的实质 理解C#中的委托需要把握两个要点: 1)委托是方法指针。 2)委托是一个类,当对其进行实例化的时候,要将引用方法作为它的构造方法的参数。 设想这样一个场景:在点对点文件传输过程当中,我们要设计一个文件传输类,该传输类起码要满足下面几项功能: 传输问题件; 按照百分制通知传输
阅读全文
摘要:建议38:小心闭包中的陷阱 先看一下下面的代码,设想一下输出的是什么? 我们的设计意图是让匿名方法(在这里表现为Lambda表达式)接受参数 i ,并输出: 0 1 2 3 4 而实际上输出为: 5 5 5 5 5 这段代码并不像我们想象的那么简单,要完全理解运行时代码是怎么运行的,首先必须理解C#
阅读全文
摘要:建议37:使用Lambda表达式代替方法和匿名方法 在建议36中,我们创建了这样一个实例程序: 实际上要完成相同的功能,还有很多种编码方式。先看一种最中规中矩的,也是最繁琐的写法: 注意:上面的语法虽然繁琐,但是我们可以从中加深对委托本质的认识:委托也是一种数据类型,跟任何FCL 中的引用类型没有差
阅读全文
摘要:建议36:使用FCL中的委托声明 FCL中存在3类这样的委托声明,它们分别是:Action、Func、Predicate。尤其是在它们的泛型版本出来以后,已经能够满足我们在实际编码过程中的大部分需求。 Action表示接受0个或多个输入参数,执行一段代码,没有任何返回值; Func表示接受0个或多个
阅读全文