1、用属性代替可访问的字段
一、.NET数据绑定只支持数据绑定,使用属性能够得到数据绑定的好处;
二、在属性的get和set访问器重可以使用lock添加多线程的支持。 html
2、readonly(运行时常量)和const(编译时常量)
一、const只可用于基元类型、枚举、字符串,而readonly则能够是任何的类型;
二、const在编译时将替换成具体的常量,这样若是在引用中同时使用了const和readonly两种值,则对readonly的再次改变将会改变设计的初衷,这是须要从新编译所更改的程序集,以从新引用新的常量值。
三、const比readonly效率高,但失去了应用的灵活性。 算法
3、is与as
一、二者都是在运行时进行类型的转换,as操做符只能使用在引用类型,而is可使用值和引用类型;
二、一般的作法是用is判断类型,而后选择使用as或强类型转换操做符(用operater定义的转换)有选择地进行。 编程
4、ConditionalAttribute代替#if #endif条件编译
一、ConditionalAttribute只用于方法级,对其余的如类型、属性等的添加都是无效的;而#if #endif则不受此限制;
二、ConditionalAttribute能够添加多个编译条件的或(OR)操做,而#if #endif则能够添加与(AND)[这里能够彻底定义为另外一个单独的符号];
三、ConditioanlAttribute定义能够放在一个单独的方法中,使得程序更为灵活。 数组
5、提供ToString()方法
一、能够更友好的方式提供用户详细的信息;
二、使用IFormatter.ToString()方法提供更灵活的定制,若是添加IFormatProvider 和ICustomFormatter接口则更有意义的定制消息输出。 安全
6、值和引用类型的区别
一、值类型不支持多态,适合存储应用程序操做的数据,而引用则支持多态,适用于定义应用程序的行为;
二、对于数组定义为值类型能够显著提升程序的性能;
三、值类型具备较少的堆内存碎片、内存垃圾和间接访问时间,其在方法中的返回是以复制的方式进行,避免暴露内部结构到外界;
四、值类型应用在以下的场景中:类型的职责主要是用于数据存储;公共接口彻底由一些数据成员存取属性定义;永远没有子类;永远没有多态行为。 服务器
7、值类型尽量实现为常量性和原子性的类型
一、使咱们的代码更易于编写和维护;
二、初始化常量的三种策略:在构造中;工厂方法;构造一个可变的辅助类(如StringBuilder)。 多线程
8、确保0为值得有效状态
一、值类型的默认状态应为0;
二、枚举类型的0不该为无效的状态;在FlagsAttribute是应确保0值为有效地状态;app
三、在字符串为为空时能够返回一个string.Empty的空字符串。 框架
9、相等判断的多种表示关系
一、ReferenceEquals()判断引用相等,须要两个是引用同一个对象时方可返回true;
二、静态的Equals()方法先进行引用判断,再进行值类型判断的;
三、对于引用类型的判断能够在使用值语义时使用重写Equals()方法;
四、重写Equals()方法时也应当重写GetHashCode()方法,同时提供operater==()操做。 ide
10、理解GetHashCode()方法的缺陷
一、GetHashCode()仅应用在基于散列的**定义键的散列值,如HashTable或Dictionary;
二、GetHashCode()应当遵循相应的三条规则:两个相等对象应当返回相同的散列码;应当是一个实例不变式;散列函数应该在全部的整数中产生一个随机的分布。
11、优先使用foreach循环语句
一、foreach能够消除编译器对for循环对数组边界的检查;
二、foreach的循环变量是只读的,且存在一个显式的转换,在**对象的对象类型不正确时抛出异常;
三、foreach使用的**须要有:具有公有的GetEnumberator()方法;显式实现了IEnumberable接口;实现了IEnumerator接口;
四、foreach能够带来资源管理的好处,由于若是编译器能够肯定IDisposable接口时,可使用优化的try…finally块;
12、默认字段的初始化优于赋值语句
一、字段生命默认会将值类型初始化为0,引用类型初始化为null;
二、对同一个对象进行屡次初始化会下降代码的执行效率;
三、将字段的初始化放到构造器中有利于进行异常处理。
十3、使用静态构造器初始化静态成员
一、静态构造器会在一个类的任何方法、变量或者属性访问以前执行;
二、静态字段一样会在静态构造器以前运行,同时静态构造器有利于异常处理。
十4、利用构造器链(在.NET 4.0已经用可选参数解决了这个问题)
一、用this将初始化工做交给另外一个构造器,用base调用基类的构造器;
二、类型实例的操做顺序是:将全部的静态字段都设置为0;执行静态字段初始化器;执行基类的静态构造器;执行当前类型的静态构造器;
将全部的实例字段设置为0;执行实例字段初始化器;执行合适的基类实例构造器;执行当前类型的实例构造器。
十5、利用using和try/finally语句来清理资源
在IDisposable接口的Dispose()方法中用GC.SuppressFinalize()可通知垃圾收集器再也不执行终结操做。
十6、尽可能减小内存垃圾
一、分配和销毁一个堆上的对象都要花费额外的处理器时间;
二、减小分配对象数量的技巧:常用的局部变量提高为字段;提供一个类,用于存储Singleton对象来表达特定类型的经常使用实例。
三、用StringBuilder进行复杂的字符串操做。
十7、尽可能减小装箱和拆箱
一、关注一个类型到System.Object的隐式转换,同时值类型不该该被替换为System.Object类型;
二、使用接口而不是使用类型能够避免装箱,即将值类型从接口实现,而后经过接口调用成员。
十8、实现标准Dispose模式
一、使用非内存资源,它必须有一个终结器,垃圾收集器在完成没有终结其的内存对象后,会将实现了终结器对象的添加到终结队列中,而后垃圾收集器会启动一个新的线程来运行这些对象上的终结器,这种防护性的变成方式是由于若是用户忘记了调用Dispose()方法,垃圾回收器老是会调用终结器方法的,这样能够避免出现非托管的内存资源不被释放引发内存泄漏的问题;
二、使用IDisposable.Dispose()方法须要作四个方面的工做:释放全部的非托管资源;释放全部的托管资源;设置一个状态标记来表示是否已经执行了Dispose();调用GC.SuppressFinalize(this)取消对象的终结操做;
三、为须要多态的类型添加一个受保护的虚方法Dispose(),派生类经过重写这个方法来释放本身的任务;
四、在须要IDisoposable接口的类型中,即便咱们不须要一个终结器也应该实现一个终结器。
十9、定义并实现接口优于继承类型
一、不相关的类型能够共同实现一个共同的接口,并且实现接口比继承更容易;
二、接口比较稳定,他将一组功能封装在一个接口中,做为其余类型的实现合同,而基类则能够随着时间的推移进行扩展。
二10、明辨接口实现和虚方法重写
一、在基类中实现一个接口时,派生类须要使用new来隐藏对基类方法的使用;
二、能够将基类接口的方法申明为虚方法,而后再派生类中实现。
二11、使用委托表达回调
一、委托对象自己不提供任何异常捕获,因此任何的多播委托调用都会结束整个调用链;
二、经过显示调用委托链上的每一个委托目标能够避免多播委托仅返回最后一个委托的输出。
二12、使用事件定义外部接口
一、应当声明为共有的事件,让编译器为咱们建立add和renmove方法;
二、使用System.ComponentModel.EventHandlerList容器来存储各个事件处理器,在类型中包含大量事件时可使用他来隐藏全部事件的复杂性。
二十3、避免返回内部类对象的引用
一、因为值类型对象的访问会建立一个该对象的副本,因此定义一个值类型的的属性彻底不会改变类型对象内部的状态;
二、常量类型能够避免改变对象的状态;
三、定义接口将访问限制在一个子集中从而最小化对对象内部状态的破坏;
四、定义一个包装器对象来限制另外一个对象的访问;
五、但愿客户代码更改内部数据元素时能够实现Observer模式,以使对象能够对更改进行校验或相应。
二十4、声明式编程优于命令式编程
能够避免在多个相似的手工编写的算法中犯错误的可能性,并提供清晰和可读的代码。
二十5、尽量将类型实现为可序列化的类型
一、类型表示的不是UI控件、窗口或者表单,都应使类型支持序列化;
二、在添加了NonSerializedAttribute的反序列化的属性时能够经过实现IDeserializationCallback的OnDeserialization()方法装入默认值;
三、在版本控制中可使用ISerializable接口来进行灵活的控制,同时提供一个序列化的构造器来根据流中的数据初始化对象,在实现时还要求SerializationFormatter异常的许可;
四、若是须要建立派生类则须要提供一个挂钩方法供派生类使用。
二十6、使用IComparable和IComparer接口实现排序关系
一、IComparable接口用于为类型实现最天然的排序关系,重载四个比较操做符,能够提供一个重载版的CompareTo()方法,让其接受具体类型做为参数;
二、IComparer用于提供有别于IComparable的排序关系,或者为咱们提供类型自己说没有实现的排序关系。
二十7、避免ICloneable接口
一、对于值类型永远不须要支持ICloneable接口,使用默认的赋值操做便可;
二、对于可能须要支持ICloneable接口的基类,应该为其创造一个受保护的复制构造器,并应当避免支持IConeable接口。
二十8、避免强制转换操做符
经过使用构造器来代替转换操做符可使转换工做变得更清晰,因为在转换后使用的临时对象,容易致使一些诡异的BUG。
二十9、只有当新版积累致使问题时才考虑使用new修饰符
三10、尽量实现CLS兼容的程序集
一、建立一个兼容的程序集须要遵循两条规则:程序集中全部公有和受保护成员所使用的参数和返回值类型都必须与CLS兼容;任何与CLS不兼容的公有和受保护成员都必须有一个与CLS兼容的替代品;
二、能够经过显式实现接口来避开CLS兼容类型检查,及CLSCompliantAttribute不会检查私有的成员的CLS兼容性。
三11、尽量实现短小简洁的方法
一、JIT编译器以方法为单位进行编译,没有被调用的方法不会被JIT编译;
二、若是将较长的Switch中的Case语句的代码替换成一个一个的方法,则JIT编译器所节省的时间将成倍增长;
三、短小精悍的方法并选择较少的局部变量能够得到优化的寄存器使用;
四、方法内的控制分支越少,JIT编译器越容易将变量放入寄存器。
三12、尽量实现小尺寸、高内聚的程序集
一、将全部的公有类以及共用的基类放到一些程序集中,把为公有类提供功能的工具类也放入一样的程序集中,把相关的公有接口打包到他们本身的程序集中,最后处理遍及应用程序中水平位置的类;
二、原则上建立两种组件:一种为小而聚合、具备某项特定功能的程序集,另外一种为大而宽、包含共用功能的程序集。
三十3、限制类型的可见性
一、使用接口来暴露类型的功能,可使咱们更方便地建立内部类,同时又不会限制他们在程序集外的可用性;
二、向外暴露的公有类型越少,将来扩展和更改实现所拥有的选择就越多。
三十4、建立大粒度的Web API
这是在机器之间的交易的频率和载荷都降到最低,将大的操做和细粒度的执行放到服务器执行。
三十5、重写优于事件处理器
一、一个事件处理器抛出异常,则事件链上的其余处理器将不会被调用,而重写的虚方法则不会出现这种状况;
二、重写要比关联事件处理器高效得多,事件处理器须要迭代整个请求列表,这样占用了更多的CPU时间;
三、事件能在运行时响应,具备更多的灵活性,能够对同一个事件关联多个响应;
四、通行的规则是处理一个派生类的事件是,重写方式较好。
三十6、合理使用.NET运行时诊断
一、System.Diagnostics.Debug\Trace\EventLog为运行时提供了程序添加诊断信息所须要的全部工具,EventLog提供入口时的应用程序能写到系统事件日志中;
二、最后不要写本身的诊断库,.NET FCL 已经拥有了咱们须要的核心库。
三十7、使用标准配置机制
一、.NET框架的System.Windows.Application类为咱们定义了创建通用配置路径的属性;
二、Application.LocalAppDataPath 和 Application.userDataPath 会生成本地数据目录和用户数据的路径名;
三、不要在ProgramFiles和Windows系统目录中写入数据,这些位置须要更高的安全权限,不要期望用户拥有写入的权限。
三十8、定制和支持数据绑定
一、BindingMananger和CurrencyManager这两个对象实现了控件和数据源之间的数据传输;
二、数据绑定的优点:使用数据绑定要比编写本身的代码简单得多;应该将它用于文本数据项以外的范围 —— 其余显示属性也能够被绑定;对于 Windowos Forms 数据绑定可以处理多个控件同步的检查相关数据源;
三、在对象不支持所需的属性时,能够经过屏蔽当前的对象,而后添加一个想要的对象来支持数据绑定。
三十9、使用.NET验证
一、ASP.NET中有五种控件来验证有效性,能够用CustomValidator派生一个新类来增长本身的认证器;
二、Windows验证须要子System.Windows.Forms.Control.Validating写一个事件处理器。
四10、根据须要选用恰当的**
一、数组有两个比较明显的缺陷:不能动态的调整大小;调整大小很是耗时;
二、ArrayList混合了一维数组和链表的特征,Queue和Stack是创建在Array基础上的特殊数组;
三、当程序更加灵活的添加和删除项时,可使更加健壮的**类型,当建立一个模拟**的类时,应当为其实现索引器和IEnumberable接口。
四11、DataSet优于自定义结构
一、DataSet有两个缺点个:使用XML序列化机制的DataSet与非.NET 代码之间的交互不是很好;DataSet是一个很是通用的容器;
二、强类型的DataSet打破了更多的设计规则,其得到的开发效率要远远高于本身编写的看上去更为优雅的设计。
四12、利用特性简化反射
经过设计和实现特性类,强制开发人员用他们来声明可被动态使用的类型、方法和属性,能够减小应用程序的运行时错误,提升软件的用户满意度。
四十3、避免过分使用反射
一、Invoke成员使用的参数和返回值都是System.Object,在运行时进行类型的转换,但出现问题的可能性也变得更多了;
二、接口使咱们能够获得一个更为清晰、也更具可维护性的系统,反射是一个很强大的晚期绑定机制,.NET框架使用它来实现Windows控件和Web控件的数据绑定。
四十4、为应用程序建立特定的异常类
一、须要不一样的异常类的惟一缘由是让用户在编写catch处理器时可以方便地对不一样的错误采起不一样的作法;
二、可能有不一样的修复行为时,咱们才应该建立多种不一样的异常类,经过提供异常基类所支持的全部构造器,能够为应用程序建立功能完整的异常类,使用InnerException属性能够保存更低级别错误条件所产生的全部错误信息。
四十5、优先选择异常安全保证
一、强异常保证在从异常中恢复和简化异常处理之间提供了最好的平衡,在操做由于异常而中断,程序的状态保留不变;
二、对将要修改的数据作防护性的复制,对这些数据的防护性复制进行修改,这中间的操做可能会引起异常,将临时的副本和原对象进行交换;
三、终结器、Dispose()方法和委托对象所绑定的目标方法在任何状况下都应当确保他们不会抛出异常。
四十6、最小化互操做
一、互操做有三个方面的代价:数据在托管堆和非托管堆之间的列举成本,托管代码和非托管代码之间切换的成本,对开发人员来讲与混合环境打交道的开发工做;
二、在interop中使用blittable类型能够有效地在托管和非托管环境中来回复制,而不受对象内部结构的影响;
三、使用In/Out特性来确保最贴切的没必要要的屡次复制,经过声明数据如何被列举来提升性能;
四、使用COM Interop用最简单的方式实现和COM组件的互操做,使用P/Invoke调用Win32 API,或者使用C++编译器的/CLR开关来混合托管和非托管的代码;
四十7、优先选择安全代码
一、尽量的避免访问非托管内存,隔离存储不能防止来自托管代码和受信用户的访问;
二、程序集在Web上运行时能够考虑使用隔离存储,当某些算法确实须要更高的安全许可时,应该将那些代码隔离在一个单独的程序集中。
四十8、掌握相关工具与资源
一、使用NUnit创建自动单元测试(集成在VS2010 中了);
二、FXCop工具会获取程序集中的IL代码,并将其与异族编码规则和最佳实践对照分析,最后报告违例状况;
三、ILDasm是一个IL反汇编工具,能够帮助咱们洞察细节;
四、Shared Source CLI是一个包含.NET框架内核和C#编译器的实现源码。
本文出自这里