} }
要允许可选参选为null。这样做是为了避免调用者调用之前需要检查参数是否null。例 如: //允许为null时的调用
DrawGeometry(brush, pen, geometry); //不允许为null时的调用
if (geometry == null) DrawGeometry(brush, pen); else DrawGeometry(brush, pen, geometry); 10.3. 属性和方法的选择
基本原则是方法表示操作,属性表示数据。如果其他各方面都一样,优先使用属性而不 是方法。
要使用属性,如果该成员表示类型的逻辑attribue
如果属性的值存储在内存中,而提供属性的目的仅仅是为了访问该值,要使用属性而不 要使用方法
如果该操作每次返回的结果不同,那么要使用方法。例如来自于.net framework的例子: //好的写法 Guid.NewGuid(); //不好的写法 DateTime.Now;
如果该操作比访问字段慢一个或多个数量级,要使用方法。 如果该操作有严重的副作用,要使用方法。 10.4. 属性的设计规范:
如果不应该让调用方法改变属性值,要创建只读属性; 不要提供只写属性;
要为所有的属性提供合理的默认值,这样可以确保默认值不会导致漏洞或效率低的代 码; 要允许用户以任何顺序来设置属性的值; 避免在属性的获取方法抛出异常。
属性的获取方法应该是个简单的操作,不应该有任何的条件。如果一个获取方法会抛出 异常,按么可能它更应该设计为方法。
10.5. 构造函数的设计规范
建议提供简单的构造函数,最好是默认构造函数。简单的构造函数增强易用性; 考虑扩展性,如果构造函数设计的不自然,建议用静态的工厂方法来替代构造函数; 要把构造函数的参数用作设置主要属性的便捷方法。如果构造函数参数仅用来设置属 性,应和属性名称相同。仅有大小写的区别;
要在构造函数中做最少的工作。任何其他处理应该推迟到需要的时候; 要在类中显示的声明公用的默认构造函数,如果这样的构造函数是必须的。
如果没有显示默认构造函数,填加有参数构造函数时往往会破坏已有使用默认构造函数 的代码;
避免在对象的构造函数内部调用虚成员。这样在扩展设计的时候会导致难以理解的现 象; 10.6. 字段设计规范
不要提供公有的或受保护的字段。代之以属性来访问字段;
要只用常量字段来表示永远不会改变的量。否则会导致兼容性问题。下面是正确的例子: public struct Int32 {
public const int MaxValue = 0x7fffffff;
public const int MinValue = unchecked((int)0x80000000); }
要用公有的静态只读字段来定义预定义的对象实例。例如: public struct Color
{
public static readonly Color Red = new Color(0x0000FF); }
10.7. 参数的设计规范
要用类结构层次中最接近基类类型来作为参数的类型,同时要保证该类型能够提供成员 所需的功能。例如:
要设计一个集合遍历的方法,那么参数应该是IEnbumerable为参数,而不应该是IList, 这样方法具有更强的适应性。
不要使用保留参数。如果将来需要更多的参数,那么可以增加重载成员。例如: //不好的写法
public void Method(string reserved, SomeOption option); //好的写法
public void Method(SomeOption option); //将来填加
public void Method(SomeOption option, string path); 10.7.1. 参数设计中枚举和布尔参数的选择规范
要用枚举。在代码阅读,书写中,枚举都比布尔的可读性好很多。例如: //使用布尔型,阅读的时候不会轻易了解参数的含义 FileStream f = File.Open(“1.txt”, true, false); //使用枚举型
FileStream f = File.Open(“1.txt”,CasingOptions.CaseSenstive, FileMode.Open); 不要使用布尔参数,除非百分之百肯定绝对不需要两个以上的值。即使此时,采用枚举 往往也可以提供更好的可读性,如上例。
考虑在构造函数中,对确实只有两种状态值的参数以及用来初始化布尔属性的参数使用 布尔类型;
10.7.2. 参数验证的规范:
要验证传给公有的,受保护的或显示成员的参数是否合法。如果验证失败,应该抛出 System.ArgutmentException或其子类;
要抛出System.ArgutmentNullException,如果传入的null,而该成员不支持null; 10.7.3. 参数传递的规范: 避免使用输出参数或引用参数;
11. 扩展性设计规范
如果没有恰当理由,不要把类密封起来。这些理由包括: A)类为静态类;
B)类的受保护成员保存了高度机密信息;
C)类继承了许多虚成员,逐个密封的代价太高,不如密封整个类; D)不要在密封类中声明保护成员或虚成员,因为无法覆盖其实现;
建议用保护成员用于高级定制。它提供了扩展性,同时也避免了公用接口过于复杂; 不要使用虚成员,除非有合适的理由;
建议只有在绝对必须的时候才用虚成员提供扩展性,并使用Template Method模式; 要优先使用受保护的虚成员,而不是公有虚成员。公有成员通用调用受保护的虚成员的方式来提供扩展性;
12. 异常处理规范
异常的思想是只对错误采用异常处理:逻辑和编程错误,设置错误,被破坏的数据,资源耗尽,等等。通常的法则是系统在正常状态下以及无重载和硬件失效状态下,不应产生任何异常。异常处理时可以采用适当的日志机制来报告异常,包括异常发生的时刻;
一般情况下不要使用异常实现来控制程序流程结构; 使用异常而不要用错误代码来报告错误;
要通过抛出异常的方式来报告操作失败。如果成员无法成功地完成它应该做的任务,那么应该抛出异常;
12.1. 异常类型选择规范
优先考虑使用System命名空间中已有的异常,而不是自己创建新的异常类型; 要使用最合理,最具针对性的异常。例如,对参数为空,应抛出 System.ArgutmentNullException,而不是System.ArgutmentException
12.2. 异常处理规范
不是百分之百确定的情况,不要吞掉异常;
建议捕获特定类型的异常,如果理解该异常在具体环境当中产生的原因; 不要捕获不应该捕获的异常,通常应该允许异常沿着调用栈传递; 进行清理工作时要用try-finally,避免使用try-catch;
要在捕获并重新抛出异常时使用空的throw语句,这是保持调用栈的最好方法 12.3. 标准异常类的使用:
12.3.1. Exception与SystemException 不要抛出这两种类型的异常;
避免捕获这两种异常,除非是在顶层的异常处理器中; 12.3.2. InvalidOperationException 对象处于不正确状态时抛出;
12.3.3. ArgumentException,ArgumentNullException,ArgumentOutOfRangeException 如果传入的是无效参数,要抛出参数异常,尽可能使用位于继承层次末尾的类型; 要在抛出异常时设置ParaName属性;
12.3.4. NullRefernceException,IndexOutOfRangeException,AccessViolationException 不要显示抛出或捕获;
12.3.5. StackOverflowException: 不要显示抛出或捕获;
12.3.6. OutOfMemoryException: