改善C#程序,提⾼程序运⾏效率的50种⽅法
⼀、⽤属性代替可访问的字段
1、数据绑定只⽀持数据绑定,使⽤属性可以获得数据绑定的好处;
2、在属性的get和t访问器重可使⽤lock添加多线程的⽀持。
⼆、readonly(运⾏时常量)和const(编译时常量)
1、const只可⽤于基元类型、枚举、字符串,⽽readonly则可以是任何的类型;
2、const在编译时将替换成具体的常量,这样如果在引⽤中同时使⽤了const和readonly两种值,则对readonly的再次改
变将会改变设计的初衷,这是需要重新编译所更改的程序集,以重新引⽤新的常量值。
3、const⽐readonly效率⾼,但失去了应⽤的灵活性。
三、is与as
1、两者都是在运⾏时进⾏类型的转换,as操作符只能使⽤在引⽤类型,⽽is可以使⽤值和引⽤类型;
2、通常的做法是⽤is判断类型,然后选择使⽤as或强类型转换操作符(⽤operater定义的转换)有选择地进⾏。
四、ConditionalAttribute代替#if#endif条件编译
1、ConditionalAttribute只⽤于⽅法级,对其他的如类型、属性等的添加都是⽆效的;⽽#if#endif则不受此限制;
2、ConditionalAttribute可以添加多个编译条件的或(OR)操作,⽽#if#endif则可以添加与(AND)[这⾥可以完全定义
为另⼀个单独的符号];
3、ConditioanlAttribute定义可以放在⼀个单独的⽅法中,使得程序更为灵活。
五、提供ToString()⽅法
1、可以更友好的⽅式提供⽤户详细的信息;
2、使⽤ng()⽅法提供更灵活的定制,如果添加IFormatProvider和ICustomFormatter接⼝则更有意
义的定制消息输出。
六、值和引⽤类型的区别
1、值类型不⽀持多态,适合存储应⽤程序操作的数据,⽽引⽤则⽀持多态,适⽤于定义应⽤程序的⾏为;
2、对于数组定义为值类型可以显著提⾼程序的性能;
3、值类型具有较少的堆内存碎⽚、内存垃圾和间接访问时间,其在⽅法中的返回是以复制的⽅式进⾏,避免暴露内部
结构到外界;
4、值类型应⽤在如下的场景中:类型的职责主要是⽤于数据存储;公共接⼝完全由⼀些数据成员存取属性定义;永远
没有⼦类;永远没有多态⾏为。
七、值类型尽可能实现为常量性和原⼦性的类型
1、使我们的代码更易于编写和维护;
2、初始化常量的三种策略:在构造中;⼯⼚⽅法;构造⼀个可变的辅助类(如StringBuilder)。
⼋、确保0为值得有效状态
1、值类型的默认状态应为0;
2、枚举类型的0不应为⽆效的状态;在FlagsAttribute是应确保0值为有效地状态;
3、在字符串为为空时可以返回⼀个的空字符串;
九、相等判断的多种表⽰关系
1、ReferenceEquals()判断引⽤相等,需要两个是引⽤同⼀个对象时⽅可返回true;
2、静态的Equals()⽅法先进性引⽤判断再进⾏值类型判断的;
3、对于引⽤类型的判断可以在使⽤值语义时使⽤重写Equals()⽅法;
4、重写Equals()⽅法时也应当重写GetHashCode()⽅法,同时提供operater==()操作。
⼗、理解GetHashCode()⽅法的缺陷
1、GetHashCode()仅应⽤在基于散列的集合定义键的散列值,如HashTable或Dictionary;
2、GetHashCode()应当遵循相应的三条规则:两个相等对象应当返回相同的散列码;应当是⼀个实例不变式;散列
函数应该在所有的整数中产⽣⼀个随机的分布;
⼗⼀、优先使⽤foreach循环语句
1、foreach可以消除编译器对for循环对数组边界的检查;
2、foreach的循环变量是只读的,且存在⼀个显式的转换,在集合对象的对象类型不正确时抛出异常;
3、foreach使⽤的集合需要有:具备公有的GetEnumberator()⽅法;显式实现了IEnumberable接⼝;实现了
IEnumerator接⼝;
4、foreach可以带来资源管理的好处,因为如果编译器可以确定IDisposable接⼝时可以使⽤优化的try…finally块;
⼗⼆、默认字段的初始化优于赋值语句
1、字段⽣命默认会将值类型初始化为0,引⽤类型初始化为null;
2、对同⼀个对象进⾏多次初始化会降低代码的执⾏效率;
3、将字段的初始化放到构造器中有利于进⾏异常处理。
⼗三、使⽤静态构造器初始化静态成员
1、静态构造器会在⼀个类的任何⽅法、变量或者属性访问之前执⾏;
2、静态字段同样会在静态构造器之前运⾏,同时静态构造器有利于异常处理。
⼗四、利⽤构造器链(在4.0已经⽤可选参数解决了这个问题)
1、⽤this将初始化⼯作交给另⼀个构造器,⽤ba调⽤基类的构造器;
2、类型实例的操作顺序是:将所有的静态字段都设置为0;执⾏静态字段初始化器;执⾏基类的静态构造器;执⾏当前
类型的静态构造器;
将所有的实例字段设置为0;执⾏实例字段初始化器;执⾏合适的基类实例构造器;执⾏当前类型的实例构造器。
⼗五、利⽤using和try/finally语句来清理资源
在IDisposable接⼝的Dispo()⽅法中⽤ssFinalize()可通知垃圾收集器不再执⾏终结操作。
⼗六、尽量减少内存垃圾
1、分配和销毁⼀个对上的对象都要花费额外的处理器时间;
2、减少分配对象数量的技巧:经常使⽤的局部变量提升为字段;提供⼀个类,⽤于存储Singleton对象来表达特定类型
的常⽤实例。
3、⽤StringBuilder进⾏复杂的字符串操作。
⼗七、尽量减少装箱和拆箱
1、关注⼀个类型到的隐式转换,同时值类型不应该被替换为类型;
2、使⽤接⼝⽽不是使⽤类型可以避免装箱,即将值类型从接⼝实现,然后通过接⼝调⽤成员。
⼗⼋、实现标准Dispo模式
1、使⽤⾮内存资源,它必须有⼀个终结器,垃圾收集器在完成没有终结其的内存对象后会将实现了终结器对象的添加
到终结队列中,然后垃圾收集器会启动⼀个新的线程来运⾏这些对象上的终结器,这种防御性的变成⽅式是因为如果⽤户忘
记了调⽤Dispo()⽅法,垃圾回收器总是会调⽤终结器⽅法的,这样可以避免出现⾮托管的内存资源不被释放引起内存
泄漏的问题;
2、使⽤e()⽅法需要做四个⽅⾯的⼯作:释放所有的⾮托管资源;释放所有的托管资源;设置⼀个状
态标记来表⽰是否已经执⾏了Dispo();调⽤ssFinalize(this)取消对象的终结操作;
3、为需要多态的类型添加⼀个受保护的虚⽅法Dispo(),派⽣类通过重写这个⽅法来释放⾃⼰的任务;
4、在需要IDisoposable接⼝的类型中,即使我们不需要⼀个终结器也应该实现⼀个终结器。
⼗九、定义并实现接⼝优于继承类型
1、不相关的类型可以共同实现⼀个共同的接⼝,⽽且实现接⼝⽐继承更容易;
2、接⼝⽐较稳定,他将⼀组功能封装在⼀个接⼝中,作为其他类型的实现合同,⽽基类则可以随着时间的推移进⾏扩
展。
⼆⼗、明辨接⼝实现和虚⽅法重写
1、在基类中实现⼀个接⼝时,派⽣类需要使⽤new来隐藏对基类⽅法的使⽤;
2、可以将基类接⼝的⽅法申明为虚⽅法,然后再派⽣类中实现。
⼆⼗⼀、使⽤委托表达回调
1、委托对象本⾝不提供任何异常捕获,所以任何的多播委托调⽤都会结束整个调⽤链;
2、通过显⽰调⽤委托链上的每个委托⽬标可以避免多播委托仅返回最后⼀个委托的输出。
⼆⼗⼆、使⽤事件定义外部接⼝
1、应当声明为共有的事件,让编译器为我们创建add和renmove⽅法;
2、使⽤andlerList容器来存储各个事件处理器,在类型中包含⼤量事件时可以使⽤他
来隐藏所有事件的复杂性。
⼆⼗三、避免返回内部类对象的引⽤
1、由于值类型对象的访问会创建⼀个该对象的副本,所以定义⼀个值类型的的属性完全不会改变类型对象内部的状
态;
2、常量类型可以避免改变对象的状态;
3、定义接⼝将访问限制在⼀个⼦集中从⽽最⼩化对对象内部状态的破坏;
4、定义⼀个包装器对象来限制另⼀个对象的访问;
5、希望客户代码更改内部数据元素时可以实现Obrver模式,以使对象可以对更改进⾏校验或相应。
⼆⼗四、声明式编程优于命令式编程
可以避免在多个类似的⼿⼯编写的算法中犯错误的可能性,并提供清晰和可读的代码。
⼆⼗五、尽可能将类型实现为可序列化的类型
1、类型表⽰的不是UI控件、窗⼝或者表单,都应使类型⽀持序列化;
2、在添加了NonSerializedAttribute的反序列化的属性时可以通过实现IDerializationCallback的OnDerialization()
⽅法装⼊默认值;
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中的Ca语句的代码替换成⼀个⼀个的⽅法,则JIT编译器所节省的时间将成倍增加;
3、短⼩精悍的⽅法并选择较少的局部变量可以获得优化的寄存器使⽤;
4、⽅法内的控制分⽀越少,JIT编译器越容易将变量放⼊寄存器。
三⼗⼆、尽可能实现⼩尺⼨、⾼内聚的程序集
1、将所有的公有类以及共⽤的基类放到⼀些程序集中,把为公有类提供功能的⼯具类也放⼊同样的程序集中,把相关
的公有接⼝打包到他们⾃⼰的程序集中,最后处理遍布应⽤程序中⽔平位置的类;
2、原则上创建两种组件:⼀种为⼩⽽聚合、具有某项特定功能的程序集,另⼀种为⼤⽽宽、包含共⽤功能的程序集。
三⼗三、限制类型的可见性
1、使⽤接⼝来暴露类型的功能,可以使我们更⽅便地创建内部类,同时⼜不会限制他们在程序集外的可⽤性;
2、向外暴露的公有类型越少,未来扩展和更改实现所拥有的选择就越多。
三⼗四、创建⼤粒度的WebAPI
这是在机器之间的交易的频率和载荷都降到最低,将⼤的操作和细粒度的执⾏放到服务器执⾏。
三⼗五、重写优于事件处理器
1、⼀个事件处理器抛出异常,则事件链上的其他处理器将不会被调⽤,⽽重写的虚⽅法则不会出现这种情况;
2、重写要⽐关联事件处理器⾼效得多,事件处理器需要迭代整个请求列表,这样占⽤了更多的CPU时间;
3、事件能在运⾏时响应,具有更多的灵活性,可以对同⼀个事件关联多个响应;
4、通⾏的规则是处理⼀个派⽣类的事件是,重写⽅式较好。
三⼗六、合理使⽤运⾏时诊断
1、raceEventLog为运⾏时提供了程序添加诊断信息所需要的所有⼯具,EventLog提供⼊
⼝时的应⽤程序能写到系统事件⽇志中;
2、最后不要写⾃⼰的诊断库,FCL已经拥有了我们需要的核⼼库。
三⼗七、使⽤标准配置机制
1、框架的ation类为我们定义了建⽴通⽤配置路径的属性;
2、ppDataPath和taPath会⽣成本地数据⽬录和⽤户数据的路径名;
3、不要在ProgramFiles和Windows系统⽬录中写⼊数据,这些位置需要更⾼的安全权限,不要指望⽤户拥有写⼊的权
限。
三⼗⼋、定制和⽀持数据绑定
1、BindingMananger和CurrencyManager这两个对象实现了控件和数据源之间的数据传输;
2、数据绑定的优势:使⽤数据绑定要⽐编写⾃⼰的代码简单得多;应该将它⽤于⽂本数据项之外的范围-其他显⽰属性
也可以被绑定;对于WindowosForms数据绑定能够处理多个控件同步的检查相关数据源;
3、在对象不⽀持所需的属性时可以通过屏蔽当前的对象然后添加⼀个想要的对象来⽀持数据绑定。
三⼗九、使⽤验证
1、中有五种控件来验证有效性,可以⽤CustomValidator派⽣⼀个新类来增加⾃⼰的认证器;
2、Windows验证需要⼦ting些⼀个事件处理器。
四⼗、根据需要选⽤恰当的集合
1、数组有两个⽐较明显的缺陷:不能动态的调整⼤⼩;调整⼤⼩⾮常耗时;
2、ArrayList混合了⼀维数组和链表的特征,Queue和Stack是建⽴在Array基础上的特殊数组;
3、当程序更加灵活的添加和删除项时,可以使更加健壮的集合类型,当创建⼀个模拟集合的类时,应当为其实现索引
器和IEnumberable接⼝。
四⼗⼀、DataSet优于⾃定义结构
1、DataSet有两个缺点个:使⽤XML序列化机制的DataSet与⾮代码之间的交互不是很好;DataSet是⼀个⾮常通
⽤的容器;
2、强类型的DataSet打破了更多的设计规则,其获得的开发效率要远远⾼于⾃⼰编写的看上去更为优雅的设计。
四⼗⼆、利⽤特性简化反射
通过设计和实现特性类,强制开发⼈员⽤他们来声明可被动态使⽤的类型、⽅法和属性,可以减少应⽤程序的运⾏时错
误,提⾼软件的⽤户满意度。
四⼗三、避免过度使⽤反射
1、Invoke成员使⽤的参数和返回值都是,在运⾏时进⾏类型的转换,但出现问题的可能性也变得更多
了;
2、接⼝使我们可以得到⼀个更为清晰、也更具可维护性的系统,反射式⼀个很强⼤的晚期绑定机制框架使⽤它来
实现Windows控件和Web控件的数据绑定。
四⼗四、为应⽤程序创建特定的异常类
1、需要不同的异常类的唯⼀原因是让⽤户在编写catch处理器时能够⽅便地对不同的错误采取不同的做法;
2、可能有不同的修复⾏为时我们才应该创建多种不同的异常类,通过提供异常基类所⽀持的所有构造器,可以为应⽤
程序创建功能完整的异常类,使⽤InnerException属性可以保存更低级别错误条件所产⽣的所有错误信息。
四⼗五、优先选择异常安全保证
1、强异常保证在从异常中恢复和简化异常处理之间提供了最好的平衡,在操作因为异常⽽中断,程序的状态保留不
变;
2、对将要修改的数据做防御性的复制,对这些数据的防御性复制进⾏修改,这中间的操作可能会引发异常,将临时的
副本和原对象进⾏交换;
3、终结器、Dispo()⽅法和委托对象所绑定的⽬标⽅法在任何情况下都应当确保他们不会抛出异常。
四⼗六、最⼩化互操作
1、互操作有三个⽅⾯的代价:数据在托管堆和⾮托管堆之间的列举成本,托管代码和⾮托管代码之间切换的成本,对
开发⼈员来说与混合环境打交道的开发⼯作;
2、在interop中使⽤blittable类型可以有效地在托管和⾮托管环境中来回复制,⽽不受对象内部结构的影响;
3、使⽤In/Out特性来确保最贴切的不必要的多次复制,通过声明数据如何被列举来提⾼性能;
4、使⽤COMInterop⽤最简单的⽅式实现和COM组件的互操作,使⽤P/Invoke调⽤Win32API,或者使⽤C++编译器
的/CLR开关来混合托管和⾮托管的代码;
四⼗七、优先选择安全代码
1、尽可能的避免访问⾮托管内存,隔离存储不能防⽌来⾃托管代码和受信⽤户的访问;
2、程序集在Web上运⾏时可以考虑使⽤隔离存储,当某些算法确实需要更⾼的安全许可时,应该将那些代码隔离在⼀
个单独的程序集中。
四⼗⼋、掌握相关⼯具与资源
1、使⽤NUnit建⽴⾃动单元测试(集成在VS2010中了);
2、FXCop⼯具会获取程序集中的IL代码,并将其与异族编码规则和最佳实践对照分析,最后报告违例情况;
3、ILDasm是⼀个IL反汇编⼯具,可以帮助我们洞察细节;
4、SharedSourceCLI是⼀个包含框架内核和C#编译器的实现源码。
四⼗九、为C#2.0做准备(这个规则现在已经没什么意义了,毕竟现在已经到了4.0)
五⼗、了解ECMA标准
本文发布于:2022-12-04 12:09:02,感谢您对本站的认可!
本文链接:http://www.wtabcd.cn/fanwen/fan/88/50988.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |