C#基础之C#代码的注意事项(上)

开发 后端
本文介绍的是我们写C#代码的一些注意事项,主要为大家介绍了50个方面的。希望对大家有帮助,一起来看。

关于代码优化的问题,之前也给大家介绍过相关的内容。下面介绍的是C#代码优化的一些注意事项,供参考。

一、用属性代替可访问的字段

1、.NET数据绑定只支持数据绑定,使用属性可以获得数据绑定的好处;

2、在属性的get和set访问器重可使用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、使用IFormatter.ToString()方法提供更灵活的定制,如果添加IFormatProvider 和ICustomFormatter接口则更有意义的定制消息输出。

六、值和引用类型的区别

1、值类型不支持多态,适合存储应用程序操作的数据,而引用则支持多态,适用于定义应用程序的行为;

2、对于数组定义为值类型可以显著提高程序的性能;

3、值类型具有较少的堆内存碎片、内存垃圾和间接访问时间,其在方法中的返回是以复制的方式进行,避免暴露内部结构到外界;

4、值类型应用在如下的场景中:类型的职责主要是用于数据存储;公共接口完全由一些数据成员存取属性定义;永远没有子类;永远没有多态行为。

七、值类型尽可能实现为常量性和原子性的类型

1、使我们的代码更易于编写和维护;

2、初始化常量的三种策略:在构造中;工厂方法;构造一个可变的辅助类(如StringBuilder)。

八、确保0为值得有效状态

1、值类型的默认状态应为0;

2、枚举类型的0不应为“无效的状态”;在FlagsAttribute是应确保0值为有效地状态;

3、在字符串为为空时可以返回一个string.Empty的空字符串;

九、相等判断的多种表示关系

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、静态字段同样会在静态构造器之前运行,同时静态构造器有利于异常处理。

十四、利用构造器链(在.NET 4.0已经用可选参数解决了这个问题)

1、用this将初始化工作交给另一个构造器,用base调用基类的构造器;

2、类型实例的操作顺序是:将所有的静态字段都设置为0;执行静态字段初始化器;执行基类的静态构造器;执行当前类型的静态构造器;

将所有的实例字段设置为0;执行实例字段初始化器;执行合适的基类实例构造器;执行当前类型的实例构造器。

十五、利用using和try/finally语句来清理资源

在IDisposable接口的Dispose()方法中用GC.SuppressFinalize()可通知垃圾收集器不再执行终结操作。

十六、尽量减少内存垃圾

1、分配和销毁一个对上的对象都要花费额外的处理器时间;

2、减少分配对象数量的技巧:经常使用的局部变量提升为字段;提供一个类,用于存储Singleton对象来表达特定类型的常用实例。

3、用StringBuilder进行复杂的字符串操作。

十七、尽量减少装箱和拆箱

1、关注一个类型到System.Object的隐式转换,同时值类型不应该被替换为System.Object类型;

2、使用接口而不是使用类型可以避免装箱,即将值类型从接口实现,然后通过接口调用成员。

十八、实现标准Dispose模式

1、使用非内存资源,它必须有一个终结器,垃圾收集器在完成没有终结其的内存对象后会将实现了终结器对象的添加到终结队列中,然后垃圾收集器会启动一个新的线程来运行这些对象上的终结器,这种防御性的变成方式是因为如果用户忘记了调用Dispose()方法,垃圾回收器总是会调用终结器方法的,这样可以避免出现非托管的内存资源不被释放引起内存泄漏的问题;l

2、使用IDisposable.Dispose()方法需要做四个方面的工作:释放所有的非托管资源;释放所有的托管资源;设置一个状态标记来表示是否已经执行了Dispose();调用GC.SuppressFinalize(this)取消对象的终结操作;

3、为需要多态的类型添加一个受保护的虚方法Dispose(),派生类通过重写这个方法来释放自己的任务;

4、在需要IDisoposable接口的类型中,即使我们不需要一个终结器也应该实现一个终结器。

十九、定义并实现接口优于继承类型

1、不相关的类型可以共同实现一个共同的接口,而且实现接口比继承更容易;

2、接口比较稳定,他将一组功能封装在一个接口中,作为其他类型的实现合同,而基类则可以随着时间的推移进行扩展。

二十、明辨接口实现和虚方法重写

1、在基类中实现一个接口时,派生类需要使用new来隐藏对基类方法的使用;

2、可以将基类接口的方法申明为虚方法,然后再派生类中实现。

二十一、使用委托表达回调

1、委托对象本身不提供任何异常捕获,所以任何的多播委托调用都会结束整个调用链;

2、通过显示调用委托链上的每个委托目标可以避免多播委托仅返回最后一个委托的输出。

二十二、使用事件定义外部接口

1、应当声明为共有的事件,让编译器为我们创建add和renmove方法;

2、使用System.ComponentModel.EventHandlerList容器来存储各个事件处理器,在类型中包含大量事件时可以使用他来隐藏所有事件的复杂性。

二十三、避免返回内部类对象的引用

1、由于值类型对象的访问会创建一个该对象的副本,所以定义一个值类型的的属性完全不会改变类型对象内部的状态;

2、常量类型可以避免改变对象的状态;

3、定义接口将访问限制在一个子集中从而最小化对对象内部状态的破坏;

4、定义一个包装器对象来限制另一个对象的访问;

5、希望客户代码更改内部数据元素时可以实现Observer模式,以使对象可以对更改进行校验或相应。

二十四、声明式编程优于命令式编程

可以避免在多个类似的手工编写的算法中犯错误的可能性,并提供清晰和可读的代码。

二十五、尽可能将类型实现为可序列化的类型

1、类型表示的不是UI控件、窗口或者表单,都应使类型支持序列化;

2、在添加了NonSerializedAttribute的反序列化的属性时可以通过实现IDeserializationCallback的OnDeserialization()方法装入默认值;

3、在版本控制中可以使用ISerializable接口来进行灵活的控制,同时提供一个序列化的构造器来根据流中的数据初始化对象,在实现时还要求SerializationFormatter异常的许可。

4、如果需要创建派生类则需要提供一个挂钩方法供派生类使用。

由于本文过长的原因,分为两篇为大家介绍,请看下一篇>>

【编辑推荐】

  1. C#多线程中lock的用法
  2. C#控件的闪烁问题解决方法总结
  3. 走进C#基础概念之事件
  4. 关于C#委托你不可不知的几件事
  5. 利用C#实现任务栏通知窗口
责任编辑:于铁 来源: saiyo
相关推荐

2011-07-06 09:46:56

C#

2009-09-01 17:25:33

初学C#编程

2009-08-06 16:13:16

C# Web Serv

2011-07-06 11:04:42

C#正则表达式

2011-04-22 09:14:26

C#委托

2009-08-25 17:59:49

C#入门

2021-03-15 08:18:23

C#反射模块

2011-07-06 10:14:46

C#

2010-12-22 10:21:17

C#基础

2011-06-08 14:22:51

延迟加载

2010-01-18 16:12:16

CC++语言

2009-08-25 17:15:50

C#隐藏C#重写C#重载

2010-01-18 14:25:19

使用C++Builde

2009-08-25 16:32:24

C#语言

2009-08-27 11:43:31

C#语法

2009-08-13 18:02:50

C#基础概念

2009-08-19 15:38:59

C#代码

2010-01-25 18:12:28

C++

2010-01-14 18:19:40

C++语言

2009-09-02 17:10:45

C#语言入门
点赞
收藏

51CTO技术栈公众号