二十五、尽可能将类型实现为可序列化的类型
1、类型表示的不是UI控件、窗口或者表单,都应使类型支持序列化;
2、在添加了NonSerializedAttribute的反序列化的属性时可以通过实现IDeserializationCallback的OnDeserialization()方法装入默认值;
3、在版本控制中可以使用ISerializable接口来进行灵活的控制,同时提供一个序列化的构造器来根据流中的数据初始化对象,在实现时还要求SerializationFormatter异常的许可;
4、如果需要创建派生类则需要提供一个挂钩方法供派生类使用。
二十六、使用IComparable和IComparer接口实现排序关系
1、IComparable接口用于为类型实现最自然的排序关系,重载四个比较操作符,可以提供一个重载版的CompareTo()方法,让其接受具体类型作为参数;
2、IComparer用于提供有别于IComparable的排序关系,或者为我们提供类型本身说没有实现的排序关系。
二十七、避免ICloneable接口
1、对于值类型永远不需要支持ICloneable接口,使用默认的赋值操作即可;
2、对于可能需要支持ICloneable接口的基类,应该为其创造一个受保护的复制构造器,并应当避免支持IConeable接口。
二十八、避免强制转换操作符
通过使用构造器来代替转换操作符可以使转换工作变得更清晰,由于在转换后使用的临时对象,容易导致一些诡异的BUG。
二十九、只有当新版积累导致问题时才考虑使用new修饰符
三十、尽可能实现CLS兼容的程序集
1、创建一个兼容的程序集需要遵循两条规则:程序集中所有公有和受保护成员所使用的参数和返回值类型都必须与CLS兼容;任何与CLS不兼容的公有和受保护成员都必须有一个与CLS兼容的替代品;
2、可以通过显式实现接口来避开CLS兼容类型检查,及CLSCompliantAttribute不会检查私有的成员的CLS兼容性。
三十一、尽可能实现短小简洁的方法
1、JIT编译器以方法为单位进行编译,没有被调用的方法不会被JIT编译;
2、如果将较长的Switch中的Case语句的代码替换成一个一个的方法,则JIT编译器所节省的时间将成倍增加;
3、短小精悍的方法并选择较少的局部变量可以获得优化的寄存器使用;
4、方法内的控制分支越少,JIT编译器越容易将变量放入寄存器。
三十二、尽可能实现小尺寸、高内聚的程序集
1、将所有的公有类以及共用的基类放到一些程序集中,把为公有类提供功能的工具类也放入同样的程序集中,把相关的公有接口打包到他们自己的程序集中,最后处理遍布应用程序中水平位置的类;
2、原则上创建两种组件:一种为小而聚合、具有某项特定功能的程序集,另一种为大而宽、包含共用功能的程序集。
三十三、限制类型的可见性
1、使用接口来暴露类型的功能,可以使我们更方便地创建内部类,同时又不会限制他们在程序集外的可用性;
2、向外暴露的公有类型越少,未来扩展和更改实现所拥有的选择就越多。
三十四、创建大粒度的Web API
这是在机器之间的交易的频率和载荷都降到最低,将大的操作和细粒度的执行放到服务器执行。
三十五、重写优于事件处理器
1、一个事件处理器抛出异常,则事件链上的其他处理器将不会被调用,而重写的虚方法则不会出现这种情况;
2、重写要比关联事件处理器高效得多,事件处理器需要迭代整个请求列表,这样占用了更多的CPU时间;
3、事件能在运行时响应,具有更多的灵活性,可以对同一个事件关联多个响应;
4、通行的规则是处理一个派生类的事件是,重写方式较好。
三十六、合理使用.NET运行时诊断
1、System.Diagnostics.Debug\Trace\EventLog为运行时提供了程序添加诊断信息所需要的所有工具,EventLog提供入口时的应用程序能写到系统事件日志中;
2、最后不要写自己的诊断库,.NET FCL 已经拥有了我们需要的核心库。
三十七、使用标准配置机制
1、.NET框架的System.Windows.Application类为我们定义了建立通用配置路径的属性;
2、Application.LocalAppDataPath 和 Application.userDataPath 会生成本地数据目录和用户数据的路径名;
3、不要在ProgramFiles和Windows系统目录中写入数据,这些位置需要更高的安全权限,不要指望用户拥有写入的权限。
三十八、定制和支持数据绑定
1、BindingMananger和CurrencyManager这两个对象实现了控件和数据源之间的数据传输;
2、数据绑定的优势:使用数据绑定要比编写自己的代码简单得多;应该将它用于文本数据项之外的范围 —— 其他显示属性也可以被绑定;对于 Windowos Forms 数据绑定能够处理多个控件同步的检查相关数据源;
3、在对象不支持所需的属性时,可以通过屏蔽当前的对象,然后添加一个想要的对象来支持数据绑定。
三十九、使用.NET验证
1、ASP.NET中有五种控件来验证有效性,可以用CustomValidator派生一个新类来增加自己的认证器;
2、Windows验证需要子System.Windows.Forms.Control.Validating写一个事件处理器。
四十、根据需要选用恰当的**
1、数组有两个比较明显的缺陷:不能动态的调整大小;调整大小非常耗时;
2、ArrayList混合了一维数组和链表的特征,Queue和Stack是建立在Array基础上的特殊数组;
3、当程序更加灵活的添加和删除项时,可以使更加健壮的**类型,当创建一个模拟**的类时,应当为其实现索引器和IEnumberable接口。
四十一、DataSet优于自定义结构
1、DataSet有两个缺点个:使用XML序列化机制的DataSet与非.NET 代码之间的交互不是很好;DataSet是一个非常通用的容器;
2、强类型的DataSet打破了更多的设计规则,其获得的开发效率要远远高于自己编写的看上去更为优雅的设计。
四十二、利用特性简化反射
通过设计和实现特性类,强制开发人员用他们来声明可被动态使用的类型、方法和属性,可以减少应用程序的运行时错误,提高软件的用户满意度。
四十三、避免过度使用反射
1、Invoke成员使用的参数和返回值都是System.Object,在运行时进行类型的转换,但出现问题的可能性也变得更多了;
2、接口使我们可以得到一个更为清晰、也更具可维护性的系统,反射是一个很强大的晚期绑定机制,.NET框架使用它来实现Windows控件和Web控件的数据绑定。
四十四、为应用程序创建特定的异常类
1、需要不同的异常类的唯一原因是让用户在编写catch处理器时能够方便地对不同的错误采取不同的做法;
2、可能有不同的修复行为时,我们才应该创建多种不同的异常类,通过提供异常基类所支持的所有构造器,可以为应用程序创建功能完整的异常类,使用InnerException属性可以保存更低级别错误条件所产生的所有错误信息。
四十五、优先选择异常安全保证
1、强异常保证在从异常中恢复和简化异常处理之间提供了最好的平衡,在操作因为异常而中断,程序的状态保留不变;
2、对将要修改的数据做防御性的复制,对这些数据的防御性复制进行修改,这中间的操作可能会引发异常,将临时的副本和原对象进行交换;
3、终结器、Dispose()方法和委托对象所绑定的目标方法在任何情况下都应当确保他们不会抛出异常。
四十六、最小化互操作
1、互操作有三个方面的代价:数据在托管堆和非托管堆之间的列举成本,托管代码和非托管代码之间切换的成本,对开发人员来说与混合环境打交道的开发工作;
2、在interop中使用blittable类型可以有效地在托管和非托管环境中来回复制,而不受对象内部结构的影响;
3、使用In/Out特性来确保最贴切的不必要的多次复制,通过声明数据如何被列举来提高性能;
4、使用COM Interop用最简单的方式实现和COM组件的互操作,使用P/Invoke调用Win32 API,或者使用C++编译器的/CLR开关来混合托管和非托管的代码;
四十七、优先选择安全代码
1、尽可能的避免访问非托管内存,隔离存储不能防止来自托管代码和受信用户的访问;
2、程序集在Web上运行时可以考虑使用隔离存储,当某些算法确实需要更高的安全许可时,应该将那些代码隔离在一个单独的程序集中。
四十八、掌握相关工具与资源
1、使用NUnit建立自动单元测试(集成在VS2010 中了);
2、FXCop工具会获取程序集中的IL代码,并将其与异族编码规则和最佳实践对照分析,最后报告违例情况;
3、ILDasm是一个IL反汇编工具,可以帮助我们洞察细节;
4、Shared Source CLI是一个包含.NET框架内核和C#编译器的实现源码。
四十九、为C#2.0做准备(这个规则现在已经没什么意义了,毕竟现在已经到了4.0 )
五十、了解ECMA标准 |