.NET 设计规范--.NET约定、惯用法与模式--4.类型设计规范

  要确每个类型有一组定义明确、相互关联的成员组成,而不仅仅是一些无关功能的随机集合。

  4.1 类型和名字空间

  要用名字空间把类型组织成一个相关的特性域的层次结构,该层次结构应该为开发人员更容易地浏览框架并找到想要的API而优化。

  避免非常深的名字空间层次。这样的层次难于浏览,因为用户不得不经常的回溯。

  避免有太多的名字空间。在最常见的场景中,框架的用户应该不需要导入许多的名字空间。只要有可能,就应该把常见场景中一起使用的类型放在一个单独的名字空间中。

  避免把为高级场景而设计的类型和为常见编程任务而设计的类型放在同一个名字空间中。

  不要不指定名字空间就定义类型

  标准子名字空间的命名

  很少使用的类型应该放在子名字空间中年,以免搅乱主名字空间。我们确定了几组类型,应该把它们从主名字空间中分离出来

  1.  .Design子名字空间

  仅用于设计时的类型应该放在名为.Design的子名字空间中。

  要用带”.Design“后缀的名字空间来容纳那些为基本名字空间提供设计时的功能的类型,例如System.Windows.Forms.Design。

  2.  .Permissions子名字空间

  许可类型应该放在名为.Permissions的子名字空间中。

  要用带”.Permissions“后缀的名字空间来容纳那些为基本名字空间提供自定义许可的类型。

  3. .Interop子名字空间

  许多框架需要支持与旧式系统的互操作性(interoperability)。把与旧式组件进行互操作的功能放到一个子名字空间中是合理的。

  要用带".Interop"后缀的名字空间来容纳那些为基本名字空间提供互操作功能的类型

  要用带".Interop"后缀的名字空间来容纳所有位于Primary Interop Assembly(PIA)中的代码。

4.2 类和结构之间的选择

  考虑定义结构而不要定义类--如果该类型的实例比较小而且生命期比较短,或者经常被内嵌在其他对象中。

  不要定义结构,除非该类型具有以下所有的特征:

  它在逻辑上代表一个独立的值,与基本类型相似

  它的实例的大小小于16个字节

  它是不可变的

  它不需要经常被装箱

4.3 类和接口之间的选择

  要优先采用类而不是接口

  要用抽象类而不是接口来解除协定月实现之间的耦合

  要定义接口,如果需要提供一个多态的值类型层次结构的话

  考虑通过定义接口来到达与多重继承向类似的效果

4.4 抽象类的设计

  不要在抽象类型中定义公有的活内部受保护的(protected-internal)构造函数

  要为抽象类型定义受保护的构造函数或内部构造函数

  要为你发布的抽象类型提供至少一个继承自给类的具体类型。例如System.IO.FileStream是System.IO.Stream抽象类的一个实现。

4.5 静态类的设计

静态类被定义为一个只包含静态成员的类。在C#2.0中,如果一个类被定义wie静态,那么它就是密封、抽象的,不能覆盖或声明任何实例成员。

  要尽量少用静态类

  不要把静态类当做是杂物箱,每一个静态类都应该有其明确的目的。

  不要在静态类中声明或覆盖实例成员

  要把静态类定义为密封的、抽象的,并添加一个私有的实例构造函数--如果你的编程语言没有内存对静态类的支持

4.6 接口的设计

  CLR不支持多重继承,但除了自以俄国基类继承之外,它还允许类型实现一个或多个接口。因此通常用接口来到达多重继承的效果。

  另一种适合定义接口的情况是在创建能够为多种类型(包括值类型)所支持的公共接口时,值类型无法自除了System.ValueType之外的类型继承,但是他们定义可以实现接口,所有为了同一个公共的基类型,使用接口时唯一的选择。

  要定义接口,如果你需要包括值类型在内的一组类型支持一些公共的API

  考虑定义接口,如果你需要让已经自其他类型继承的类型支持该接口提供的功能。

  避免使用记号接口(没有成员的接口) 如果你需要给一个具备某种特征的类做记号,一般来说,最好使用自定义attribute而不是使用接口。

  要为接口提供至少一个实现该接口的类型 System.Collections.ArrayList是System.Collections.IList接口的实现

  要为你定义的每个即可提供至少一个使用该接口的API(一个以该接口为参数的方法,或是一个类型为该接口的属性)

    例如List<T>.Srot使用了IComparer<T>接口

  不要给已经发行的接口在添加成员,你应该创建一个新的接口。

4.7 结构的设计

  不要为结构提供默认的构造函数

  要确保当所有的实例数据都为零,false或null时,结构仍然处于有效状态

  要为值类型实现IEuatable<T>

  不要显示地扩展System.ValueType,事实上大多数编程语言不允许这样做。

4.8 枚举的设计 

  要用枚举来加强那些表示值的集合的参数、属性以及返回值的类型性

  要优先使用枚举而不要使用静态常量

  不要把枚举用于开放的集合(比如操作系统的版本、朋友的名字等)

  不要提供为了今后使用而保留的枚举值

  避免显示的暴露只有一个值的枚举

  不要吧Sentinel值包含在枚举值中 sentinel值是用来跟踪枚举状态的值,而不属于枚所表示的值的集合。

  要为简单枚举类型提供零值,可以考虑把该值成为"None"之类的东西。如果这样的值不适用于某个特定的枚举,那么应该把枚举中最常用的默认值赋值为零。

  考虑用Int32作为枚举类型的基本实现类型,除非下面的任何一条成立:

    该枚举是一个标记枚举,而你有超过32个标记,或预计今后会用更多的标记

    为了更方便地与需求不同大小的枚举的非托管代码进行互操作,基本的实现类型必须是Int32之外的类型。

    更小的底层实现类型可能会节省相当的空间。如果你预计枚举将主要用于控制流的参数,那么枚举的大小几乎不会造成什么区别。如果属于下面的情况,那么空间的节省肯尼个相当可观:

      你预计枚举将被用作结构或类的字段,而且该结构或类会频繁地实例化

      你预计用户会创建包含该枚举实例的大型数组或集合

      你预计该枚举的大量实例会被序列化

  要用复数名词或名词短语来命名标记枚举,用单数名词或名词短语来命名简单枚举

  不要直接扩充System.Enum。

  8.1标记枚举的设计

  要对标记枚举使用System.FlagsAttribute。不要把该attribute用于简单的枚举

  要用2的幂次方作为标记枚举的值,这样可以通过按位或操作自由地组合它们。

  考虑为常用的标记组合提供特殊的枚举值

  位操作时一个高级概念,对简单的任务来说 应该不是必须的。

  避免让创建的标记枚举包含某些无效的组合

  避免把零用作为标记枚举的值,除非该值表示”所有标记都被清除“,而且按下一条规范进行设当的命名。

  要把标记枚举的零值命名为None。对标记枚举来说,该值必须始终意味着"所有标记均被清除"

  8.2 给枚举添加值

  考虑给枚举添加值,尽管有那么一点兼容的风险

4.9 嵌套类型

  嵌套类型是一个定义在另一个类型的作用域内的类型,另一个类型称为外层(enclosing type)。嵌套类型能够访问它的外层类型的所有成员。

 

posted on 2012-04-09 14:31  lufangtao  阅读(321)  评论(0编辑  收藏  举报

导航