第 8 章:使用规范

8.1 数组

  要在公共 API 中优先使用集合,避免使用数组。

  不要使用只读的数组字段。虽然字段本身是只读的,用户不能修改它们,但用户可以修改数组中的元素。

  考虑使用不规则数组,而不要使用多维数组。

  

8.2 修饰属性

  要在命名自定义修饰属性类时添加“Attribute”后缀。

  要在定义自己的修饰属性时使用 AttributeUsageAttribute。

  要将必填参数定义为只读属性。

  要提供构造函数参数来对必填参数进行初始化。每个参数的名字应该与对应属性的名字相同(但大小写会不同)。

  避免提供构造函数参数来对可选属性(可选参数)进行初始化。

  避免对自定义修饰属性的构造函数进行重载。

  要尽可能将自定义修饰属性类密封起来。这样会使对修饰属性的查找更快。

  

8.3 集合

  不要在公共 API 中使用弱类型集合。

  不要在公共 API 中使用 ArrayList 或 List<T>。

  不要在公共 API 这种使用 Hashtable 或 Dictionary<TKey, TValue>。

  不要使用 IEnumerator<T>、IEnumerator 或实现了这两个接口之一的任何其他类型,除非是作为 GetEnumerator 方法的返回类型。

  不要在同一类型中同时实现 IEnumerator<T> 和 IEnumerable<T>。对非泛型接口 IEnumerator 和 Enumerable 来说也同样如此。

  要用最泛的类型来作为参数类型。大多数以集合为参数的成员都使用 IEnumerable<T> 接口。

  避免使用 ICollection<T> 或 ICollection 来做参数 - 如果其目的仅仅只是为了访问该接口的 Count 属性。

  不要提供可设置的集合属性。

  要用 Collection<T> 或其子类 - 如果属性或返回值表示可读写的集合。

  要用 ReadOnlyCollection<T> 或其子类,在少数情况下用 IEnumerable<T>,如果属性或返回值表示只读的集合。

  考虑使用泛型集合基类的子类,而不要直接使用该集合。

  考虑用 Collection<T> 或 ReadOnlyCollection<T> 的子类来作为常用方法和常用属性的返回值。

  考虑使用有键集合 - 如果集合这种存储的元素都有独一无二的键值(名字、ID 等等)。有键集合是一个能同时以整数值和键值为索引来访问的集合,在实现时通常会让它们派生自 KeyedCollection<TKey, TItem>。

  不要从集合属性或以集合为返回值的方法中返回 null。而要返回一个空集合或空数组。

  不要让属性返回快照集合,属性应该返回实况集合。

  要用快照集合或实况的 IEnumerable<T>(或它的子类)来表示不稳定的集合(也就是说,无需显式地修改就可能会发生改变的集合)。

  不要让属性返回快照集合,属性应该返回实况集合。

  要用快照集合或实况的 IEnumerable<T> (或它的子类)来表示不稳定的集合(也就是说,无需显式地修改就可能会发生改变的集合)。

  要在设计新的集合时实现 IEnumerable<T>。如果合理,还可以考虑实现 ICollection<T> 甚至 IList<T>。

  考虑实现非泛型集合接口(IList 和 ICollection)- 如果经常需要把集合传给以这些参数为输入的 API。

  避免为类型实现集合接口 - 如果类型的 API 很复杂,而且与集合的概念无关。

  不要继承自非泛型的集合基类,比如 CollectionBase。要使用 Collection<T>,ReadOnlyCollection<T>,以及 KeyedCollection<TKey, TItem>。

  要在为类型命名时添加“Dictionary”后缀 - 如果类型实现了 IDictionary 或 IDictionary<TKey, TValue> 接口。

  要在为类型命名时添加“Collection”后缀 - 如果类型实现了 IEnumerable(或它的任何子类)接口,并且类型表示的是一个元素列表。

  要在命名自定义的数据结构时,使用合适的数据结构名。

  避免在为集合抽象命名时添加代表其具体实现的后缀,比如“LinkedList”或“Hashtable”。

  考虑用集合元素的类型名作为集合名字的前缀。

  考虑给只读集合的名字添加“ReadOnly”前缀,如果今后有可能会增加与之对应的可写集合,或与之对应的可写集合在框架中已经存在。

8.4 DateTime 和 DateTimeOffset

  要使用 DateTimeOffset - 如果想要表示一个精确地时间点。例如,用它来计算现在的时间、事务开始的时间、文件修改的时间、记录事件的时间,等等。如果不知道时区,那么就使用 UTC。与 DateTime 更实用的场景相比,上述场景更加常见,因此在默认情况下应该使用 DateTimeOffset。

  要在任何不适合使用绝对时间点的情况下使用 DateTime,比如能适用于不同时区的商店开门的时间。

  要在不知道时区或有时候不知道时区的情况下使用 DateTime。如果数据来自老式系统,那么可能会发生这种情况。

  不要在能够使用 DateTimeOffset 的时候使用 DateTimeKind。

  要用 DateTime 来表示所有的日期(比如生日),并将时间部分设为 00:00:00.不要用 DateTimeOffset 来表示日期。

8.5 ICloneable

  不要实现 ICloneable。

  不要在公共 API 中使用 ICloneable。

  考虑为需要克隆机制的类型定义 Clone 方法。一定要在文档中明确说明该方法执行的是深复制还是浅复制。

  

8.6 IComparable<T> 与 IEquatable<T>

  要为值类型实现 IEquatable<T>。

  要在实现 IEquatable<T>.Equals 时,同样遵循为覆盖 Object.Equals 而制定的规范。

  要在实现 IEquatable<T> 的同时覆盖 Object.Equals。

  考虑在实现 IEquatable<T> 的同时重载 operator == 和 operator !=。

  要在实现 IComparable<T> 的同时实现 IEquatable<T>。

  考虑在实现 IComparable<T> 的同时重载比较操作符(<、>、<=、>=)。

 

8.7 IDisposable

8.8 Nullable<T>

  考虑用 Nullable<T> 来表示那些可能不存在的值(比如可选的值)。举个例子,如果要从数据库中返回一个强类型的记录,并且其中有一个属性对应的是表中一个可有可无的列,那么就应该使用 Nullable<T>。

  不要使用 Nullable<T> - 除非在类似的情况下你会因为引用类型可以为 null 而考虑用引用类型来代替它。例如,不应该用 null 来表示可选参数。

  避免用 Nullable<bool> 来表示通用的具有三种状态的值。Nullable<bool> 应该只用来表示真正可选的布尔值:true、false 以及不可用。如果至少是想表示具有三种状态的值(例如 yes、no、cancel),那么可以考虑使用枚举。

  避免使用 System.DBNull。要优先使用 Nullable<T>。

8.9 Object

  要在覆盖 Object.Equals 方法时,遵守它定义的契约。

  要在覆盖 Equaals 方法的同时覆盖 GetHashCode 方法。

  考虑在覆盖 Object.Equals 方法的同时实现 IEquatable<T> 接口。

  不要从 Equals 方法中抛出异常。

  要覆盖值类型的 Equals 方法。

  要通过实现 IEquatable<T> 来提供一个以该值类型本身为参数的 Equals 重载方法。

  考虑覆盖 Equals 以提供值相等语义 -  如果引用类型表示的是一个值。例如,对那些表示数值或其他数学试题的引用类型来说,可以考虑覆盖 Equals 方法。

  不要为可变的引用类型实现值相等语义。

  要覆盖 GetHashCode 方法 - 如果覆盖了 Object.Equals 方法。

  要确保对任何两个对象来说,如果 Object.Equals 方法返回 true,那么它们的 GetHashCode 方法的返回值也应该相同。

  要竭尽所能让类型的 GetHashCode 方法产生随机分布的散列码。

  要确保无论怎么更改对象,GetHashCode 都会返回完全相同的值。

  避免从 GetHashCode 方法中抛出异常。

  要覆盖 ToString 方法 - 只要能返回既有用,又易于让人阅读的字符串。

  要尽量让 ToString() 方法返回短小的字符串。

  考虑为每一个实例返回一个独一无二的字符串。

  要使用易于阅读的名字,而不要使用虽然独一无二,但却让人无法理解的 ID。

  要在返回与区域性有关的信息时,根据当前线程的区域性来对字符串进行格式化。

  要提供重载方法 ToString(string format) 或实现 IFormattable 接口 - 如果 ToString() 方法返回的字符串与区域性有关,或者有多种方法来对字符串进行格式化。例如,DateTime 既提供了重载方法,又实现了 IFormattable 接口。

  不要从 ToString() 方法返回空字符串或 null。

  避免从 ToString() 方法中抛出异常。

  要确保 ToString() 方法不会产生副作用。

  要通过 ToString() 的覆盖方法来报告与安全性有关的信息,前提是必须先获得相应的许可。如果无法获得许可,那么应该在返回的字符串中去除对安全性有关的信息。

  考虑让 ToString 方法输出的字符串能够为该类型的解析方法正确地解析。

  

8.10 序列化

  要在设计心得类型时考虑到序列化。

  考虑让类型支持数据协定序列化 - 如果需要在 Web 服务中使用该类型,或需要在 Web 服务中对该类型进行持久化。

  考虑让类型只支持 XML 序列化,或同时支持数据协定序列化和 XML 徐丽华 - 如果需要在徐丽华类型时生成的 XML 的格式有更多的控制。

  考虑让类型支持运行时序列化 - 如果需要跨越 .NET Remoting 的边界传输类型。

  不要仅仅为了进行一般的持久化而支持 XML 序列化或运行时序列化。应该优先支持数据协定序列化。

  考虑将类型中的成员定义为公有的 - 如果类型会被用于不完全可信的环境。

  要为所有应用了 DataMemberAttribute 的属性实现 getter 和 setter。为了能够让数据协定序列化程序对类型进行序列化,属性必须有 getter 和 setter。如果类型不会用于不完全可信的环境中,那么 getter 和 setter 中的一个或两个可以不是公有的。

  要用序列化毁掉函数来对反序列的实例进行初始化。

  考虑使用 KnownTypeAttribute 来表示那些在反序列化复杂的对象图时应该会用到的具体类型。

  要在创建或改变可序列化的类型时考虑向后兼容性和向前兼容性。

  考虑为了支持新老版本的双向转换而实现 IExtensibleDataObject。

  避免在设计类型时特别考虑 XML 序列化,除非有强烈的理由要对生成的 XML 内容加以控制。XML 序列化技术已经被前面介绍的数据协定序列化技术所取代。

  考虑实现 IXmlSerializable 接口 - 如果应用 XML 序列化修饰属性后生成的 XML 内容还不能满足需要。这个接口有两个方法,ReadXml 和 WriteXml,可以用他们来完全控制生成的 XML 内容。还可以给类型应用 XmlSchemaProviderAttribute 来对生成的 XML 架构加以控制。

  考虑支持运行时序列化 - 如果类型会被用于 NET Remoting。例如,由于 System.AddIn 命名空间用到了 .NET Remoting,因此在 System.AddIn 的外接程序(add-in)之间传输的所有类型都必须支持运行时序列化。

  考虑实现运行时序列化模式 - 如果想要完全控制序列化的整个过程。例如,想在序列化或反序列的时候进行数据转换。

  要将序列化构造函数定义为受保护的,并提供两个参数,参数的类型和命名要和下面的样例代码完全相同。

  要显式地实现 ISerializable 接口的成员。

  要给 Iserializable.GetObjectData 的实现应用一个链接要求,这样做是为了保证只有完全可信的代码和运行时序列化程序才能访问该成员。

8.11 Uri

  要使用 System.Uri 来表示 URI 和 URL 数据。

  考虑为最常用的带 System.Uri 参数的成员提供基于字符串的重载成员。

  不雅不假思索地为所有基于 System.Uri 的成员提供基于字符串的重载成员。

  要调用基于 System.Uri 的重载成员 - 如果有的话。

  不要在字符串中存储 URI/URL 数据。

  

8.12 System.Xml 的使用

  不要用 XmlNode 或XmlDocument 来表示 XML 数据。要尽量使用 IXPathNavigable、XmlReader、XmlWriter 或 XNode 的子类型。XmlNode 和 XmlDocument 的设计目的并不是为了公开暴露给外界的 API。

  要在接受 XML 或返回 XML 的成员中,以 XmlReader、IXPathNavigable 或 XNode 的子类型为输入或输出。

  不要从 XmlDocument 派生子类 - 如果想要创建的类型表示下层对象模型或数据源的 XML 视图。

  

8.13 相等性操作符

  不要只重载相等性操作符中的一个。

  要确保 Object.Equals 与相等性操作符具有完全相同的语义及相近的性能。

  避免从相等性操作符中抛出异常。

  要重载值类型的相等性操作符 - 如果相等性是有意义。

  避免重载可变引用类型的相等性操作符。

  避免重载引用类型的相等性的相等性操作符 - 如果其实现会引用相等性的实现慢得多。

  

《.NET 设计规范》第 8 章:使用规范的更多相关文章

  1. 《.NET 设计规范》第 3 章 命名规范

    <.NET 规范>第 3 章 命名规范 3.1 大小写约定 要把 PascalCasing 用于由多个单词构成的命名空间.类型以及成员的名字. 要把 camelCasing 用于参数的名字 ...

  2. Python第一章-编码规范

    Python的基础知识 一.编码规范 PEP8[^ 注] 编码规范 Guido的关键点之一是:代码更多是用来读而不是写.编码规范旨在改善Python代码的可读性. 风格指南强调一致性.项目.模块或函数 ...

  3. .NET设计规范————命名规范

    NET设计规范:约定.惯用法与模式———命名规范 前言:          最近在看<.NET设计规范:约定.惯用法与模式>一书,主要还是讲.NET的设计规范,以前对这一块也不是特别在意, ...

  4. NET设计规范(二) 命名规范

    http://blog.csdn.net/richnaly/article/details/6280294 第2章       命名规范 2.1.   大小写约定 2.1.1.    标识符的大小写规 ...

  5. PHP编程规范

    好的编程规范不仅是对阅读者的负责,也是对自身的负责: ----割---- 一直以来我都是以php函数的风格来写php,所有变量,函数,类都使用小写,单词之间以下划线隔开,一直比较排斥驼峰式的代码规范, ...

  6. UED团队规范设计参考及建议

    公司产品线逐渐增多,变动频繁且并行开发,常常需要设计与开发能够快速的做出响应.同时这类产品中有存在很多类似的页面以及组件,可以通过抽象得到一些稳定且高复用性的内容.通过模块化的解决方案,降低冗余的生产 ...

  7. MYSQL数据库设计规范与原则

    MYSQL数据库设计规范 1.数据库命名规范 采用26个英文字母(区分大小写)和0-9的自然数(经常不需要)加上下划线'_'组成; 命名简洁明确(长度不能超过30个字符); 例如:user, stat ...

  8. 设计神器 - 摹客设计系统上线了 | 晒出你的设计规范,赢iPad Pro!

    在国内,设计规范也许还是个不太常用的概念,但是如果你正好有参与互联网公司的产品设计,你应该早就已经体会到设计规范的重要性了.UI设计师总是要花费大量的时间和精力向开发描述一大堆设计细节,但是产品最后呈 ...

  9. 设计规范VS设计创造力,谁更胜一筹?

    设计规范和设计创造力哪个更重要?这是一个颇具争议性的话题.如果是3年前问我这个问题我会毫不犹豫的选择设计创造力,毫无疑问,一个好的设计创造力真的是可以让人像打了鸡血一样疯狂. 原来在上大学的时候,我就 ...

  10. 【转】MYSQL数据库设计规范与原则

    转载出:http://www.cnblogs.com/lovekingly/p/5044278.htmlMYSQL数据库设计规范 1.数据库命名规范 采用26个英文字母(区分大小写)和0-9的自然数( ...

随机推荐

  1. 终于理解kalman滤波

    2017拜拜啦,怎么过元旦呢?当然是果断呆实验室过... 应该是大二的时候首次听说kalman,一直到今天早上,我一看到其5条"黄金公式",就会找各种理由放弃,看不懂呀...但是研 ...

  2. POJ 2209 The King(简单贪心)

    The King Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7499   Accepted: 4060 Descript ...

  3. BZOJ 2456: mode(新生必做的水题)

    2456: mode Time Limit: 1 Sec  Memory Limit: 1 MB Submit: 4868  Solved: 2039 [Submit][Status][Discuss ...

  4. c语言基础学习09_关于复合类型的复习

    =============================================================================struct A{ char array[10 ...

  5. 安装Ruby、Sass与Compass

    sass基于Ruby语言开发而成,因此安装sass前需要安装Ruby.(注:mac下自带Ruby无需在安装Ruby!) window下安装SASS首先需要安装Ruby,先从官网下载Ruby并安装.安装 ...

  6. Node类型知识大全

    Node类型 1.节点关系 每个节点都有一个childNodes属性,其中保存着一个NodeList对象.NodeList是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点.请注意, ...

  7. YUI 的模块信息配置优先级关系梳理

    背景 YUI的配置参数较多, 可以在好几个地方配置一个module的相关信息, 如: //在全局配置, 所以YUI实例共享 YUI_config = { modules: { 'w-autcomple ...

  8. AMD规范学习笔记

    背景 NodeJS的一套比较简洁 Moudles 规范, 使得在服务器端的模块化变得更加简单.很长一段时间,很多公司或者项目都有自己的一套模块化机制, 却未能形成一套统一的标准, NodeJS的Mou ...

  9. vuex的使用

    vue现在越来越火,不单单可以写简单的小项目,也可以写大中型的项目.但是项目大了,项目之间的数据传递就会变得复杂,那么问题来了?在一个大型项目中,多个组件要公用同一个或多个数据,我们如何保证每个组件获 ...

  10. jdbc、Mybatis插入数据主键回显的实现方法

    插入数据的时候,往往需要获取主键值.但是有时候主键是自增长的那么,就不太适用手动添加主键值了,此时需要一种可以回显主键参数的方法, 下面以jdbc.mybatis的实现举例 此时使用的是jdbc的话或 ...