《.NET 设计规范》第 5 章:成员设计

5.1 成员设计的通用规范

  要尽量用描述性的参数名来说明在较短的重载中使用的默认值。

  避免在重载中随意地改变参数的名字。如果两个重载中的某个参数表示相同的输入,那么该参数的名字应该相同。

  避免使重载成员的参数顺序不一致。在所有的重载中,同名参数应该出现在相同的位置。

  要把最长的重载成员定义成重载成员中唯一的虚成员。

  不要用 ref 或 out 修饰符来对成员进行重载。

  不要定义这样的重载:位于同一个位置的参数有相似的类型但却有不同的语义。

  要允许在传递参数时将可选参数设为 null。

  要优先使用成员重载,而不是定义有默认参数的成员。

  显式地实现接口成员。

  避免显示地实现接口成员 - 如果没有很强的理由。

  考虑显式地实现接口成员 - 如果希望接口成员只能通过该接口来调用。

  考虑通过显式地实现接口成员的方式来模拟变体。

  考虑在需要隐藏一个成员并增加另一个名字更合适的等价成员时,显式地实现接口成员。

  不要把接口成员的显示实现当做安全壁垒。

  要为显式实现的接口成员提供具有相同功能的受保护的虚成员 - 如果希望让派生类对该功能进行定制。

  考虑使用属性 - 如果该成员表示类型的一种逻辑属性。

  要使用属性而不要使用方法 - 如果属性的值储存在进程内存中,而且提供属性的目的仅仅是为了访问该值。

  要在下列情况中时使用方法而不要使用属性:

    该操作比字段访问要慢几个数量级;

    该操作是一个转换操作,如:object.ToString() 方法;

    该操作在每次调用时都返回不同的结果,即使传入的参数不变。如:Guid.NewGuid 方法在每次都返回不同的值;

    该操作有严重的、显而易见的副作用;

    该操作返回内部状态的一个副本(这不包括那些在栈上返回的值类型对象的副本);

    该操作返回一个数组。

5.2 属性的设计

  要创建只读属性 - 如果调用方不应该改变属性的值。

  不要提供只写属性,也不要让 setter 的可访问性比 getter 更广。

  要为所有的属性提供合理的默认值,这样可以确保默认值不会导致安全漏洞或效率低下的代码。

  要允许用户以任何顺序来设属性的值,即使这可能会使对象在短时间内处于无效状态。

  要保留属性原来的值,如果属性的 setter 抛出异常。

  避免在属性的 getter 中抛出异常。

  考虑通过索引器的方式让用户访问存储在呢不数组中的数据。

  考虑为代表元素集合的类型提供索引器。

  避免使用有一个以上参数的索引属性。

  避免用 System.Int32、System.Int64、System.String、System.Object、枚举或泛型参数之外的类型来作索引器的参数。

  要将 Item 名称用于索引属性,除非有明显更好的名字(例如 System.String 的 Chars 属性)。

  不要同时提供语义上等价的索引器和方法。

  不要在一个类型中提供具有不同名字的索引器。

  不要使用非默认的索引属性。

  考虑在高层 API 的属性值被修改时触发属性改变的通知事件。

  考虑在属性值被外界修改时触发通知事件。

  

5.3 构造函数的设计

  考虑提供简单的构造函数,最好是默认构造函数。

  考虑用静态工厂方法来代替构造函数 - 如果无法让想要执行的操作的语义与新实例的构造函数直接对应,或者遵循构造函数的设计规范会让人感觉不合理。

  要把构造函数的参数列表当做设置主要属性的快捷方法。

  要用相同的名字来命名构造函数的参数和属性 - 如果定义该构造函数参数的目的就是为了设置对应的属性。

  要在构造函数中做最少的工作。

  要在适当的时候从实例构造函数中抛出异常。

  要在类中显式地声明公有的默认构造函数 - 如果这样的构造函数是必需的。 

  避免在结构中显式地定义默认构造函数。

  避免在对象的构造函数内部调用虚成员。

  要把静态构造函数声明为私有。

  不要从静态构造函数中抛出异常。

  考虑以内联的形式来初始化静态字段,而不要显式地定义静态构造函数,这是因为运行库能够对那些没有显示定义静态构造函数的类型进行性能优化。

5.4 事件的设计

  要在事件中使用术语“raise”,而不要使用“fire”或“trigger”。

  要用 System.EventHandler<T> 来定义事件处理函数,而不是手工创建新的委托来定义事件处理函数。

  考虑用 EventArgs 的子类来做事件的参数,除非百分之百确信该事件不需要给事件处理方法传递任何数据,在这种情况下可以直接使用 EeventArgs。

  要用受保护的虚方法来触发事件。这只适用于非密封类中的非静态事件,不适用于结构、密封类以及静态事件。

  要让触发事件的受保护的方法带一个参数,该参数的类型为事件参数类,该参数的名字应该为 e。

  不要在触发非静态事件时把 null 作为 sender 参数传入。

  要在触发静态事件时把 null 作为 sender 参数传入。

  不要在触发事件时把 null 作为数据参数传入。

  考虑触发能够被最终用户取消的事件,这适用于前置事件。

  要把事件处理函数的返回类型定义为 void。

  要用 object 作为事件处理函数的第一个参数的类型,并将其命名为 sender。

  要用 System.EventArgs 或其子类作为事件处理函数的第二个参数的类型,并将其命名为 e。

  不要在事件处理函数中使用两个以上的参数。

  

5.5 字段的设计

  不要提供公有的或受保护的实例字段。

  要用常量字段来表示永远不会改变的常量。

  要用公有的静态只读字段来定义预定义的对象实例。

  不要把可变类型的实例赋值给只读字段。

  

5.6 扩展方法

  避免草率地定义扩展方法,尤其是为别人的类型定义扩展方法。

  考虑在下面的场景中使用扩展方法。

  为一个接口的所有实现提供相关的辅助方法,而且这些功能可以通过核心接口来表达。

  如果增加一个实例方法会引入对其它类型的依赖关系,而依赖关系会破坏依赖关系的管理规则,那么应该使用扩展方法。

  避免为 System.Object 定义扩展方法。

  不要把扩展方法和被扩展的类型放在同一个命名空间中 - 除非是为了把方法增加到接口中,或是为了对依赖关系进行管理。

  避免在定义两个扩展方法时使用相同的签名,即使它们位于不同的命名空间。

  考虑把扩展方法和被扩展的类型放在同一个命名空间 - 如果被扩展的类型是接口,而且该扩展方法的设计目的就是要用于大多数的情况甚至是所有的情况。

  不要把实现某个特性的扩展方法放在一个通常与其他特性相关联的命名空间中。相反,该特性属于哪个命名空间,就应该把对应的扩展方法放在那里。

  避免使用太宽泛的名字(例如“Extensions”)来给扩展方法专用的命名空间命名,要使用更具描述性的名字(比如“Routing”)。

  

5.7 操作符重载

  避免定义操作符重载,除非该类型让人感觉像个基本(内置)类型。

  考虑在让人感觉应该想基本类型的类型中定义操作符重载。

  要为表示数值的结构(比如 System.Decimal)定义操作符重载。

  不要在定义操作符重载时耍小聪明。

  不要提供操作符重载,除非至少有一个操作数的类型是定义该操作符重载的类型。

  要以对称的方式来重载操作符。

  考虑为每个重载过的操作符提供对应的方法,并用容易理解的名字来命名。

  不要提供类型转换操作符 - 如果没有明确的用户需求。

  不要在定义类型转换操作符时超越类型所在的领域。

  不要提供隐式类型转换操作符 - 如果这样的类型转换可能会丢失精度。

  不要从隐式的强制类型转换操作符中抛出异常。

  要抛出 System.InvalidCastException - 如果对强制类型转换操作符的调用会丢失精度而该操作符承诺不丢失精度。

5.8 参数的设计

  要用类层次结构中最接近基类的类型作为参数的类型,同时要保证该类型能够提供成员所需的功能。

  不要使用保留参数。

  不要把指针、指针数组及多维数组作为公有方法的参数。

  要把所有的输出参数放在所有以值方式和以引用方式传递的参数的后面(不包括参数数组),即使这样会在重载成员之间导致参数顺序不一致也要如此。

  要在覆盖成员或者实现接口成员时保持参数命名的一致。

  要用枚举 - 如果不这样做会导致参数中有两个或两个以上的布尔类型。

  不要使用布尔参数,除非百分之百肯定绝对不需要两个以上的值。

  考虑在构造函数中,对确实只有两种状态的参数以及用来初始化布尔属性的参数使用布尔类型。

  要对传给共有的、受保护的或显式实现的成员的参数进行验证。如果验证失败,那么应该抛出 System.ArgumentException 或其子类。

  要抛出 ArgumentNullExcepytion - 如果传入的是 null 而该成员不支持 null。

  要验证枚举参数。

  不要用 Enum.IsDefined 来检查枚举的范围。

  要清楚地知道传入的可变参数可能会在验证后发生改变。

  避免使用输出参数或引用参数。

  不要以引用方式传递引用类型。

  考虑给数组参数增加 params 关键字 - 如果预计用户会传入为数不多的数组元素。

  避免使用 params 数组参数 - 如果绝大多数时候调用方要传入的数组元素本来就已经在一个数组中了。

  不要使用 params 数组参数 - 如果要在成员中对数组进行修改。

  考虑在简单的重载中使用 params 关键字,尽管更复杂的重载不能用 params 关键字。

  要对参数进行合理的排序,以便使用 params 关键字。

  考虑在对性能要求非常高的 API 中为参数数量较少的调用提供特殊的重载和相应的实现。

  要注意传入的 params 数组参数可能是 null。

  不要使用 varargs 方法,又称省略号。

  要为任何以指针为参数的成员提供一个替补成员,这是因为指针不符合 CLS 规范。

  避免对指针参数进行高开销的检查。

  要在设计用到指针的成员时遵循与指针相关的常用约定。

  

《.NET 设计规范》第 5 章:成员设计的更多相关文章

  1. NET设计规范二:类型成员设计

    http://www.cnblogs.com/yangcaogui/archive/2012/04/20/2459567.html 接着 → .NET设计规范一:设计规范基础 上一篇,我们来了解下类型 ...

  2. 【二代示波器教程】第12章 示波器设计—DAC信号发生器的实现

    第12章      示波器设计—DAC信号发生器的实现 本章节为大家讲解二代示波器中信号发生器的实现.这个功能还是比较实用的,方便为二代示波器提供测试信号.实现了正弦波,方波和三角波的频率,幅度以及占 ...

  3. 【安富莱二代示波器教程】第6章 示波器设计—双通道ADC驱动

    第6章        示波器设计—双通道ADC驱动 本章节为大家讲解示波器的ADC驱动,采用STM32自带ADC实现.关于STM32F429的ADC,可以说处处有地雷,不小心就踩上了,如果简单的使用, ...

  4. ASP.Net MVC 5 高级编程 第7章 成员资格、授权和安全性

    第7章 成员资格.授权和安全性 7.1 安全性 ASP.NET MVC 提供了许多内置的保护机制(默认利用 HTML 辅助方法和Razor 语法进行 HTML编码以及请求验证等功能特性,以及通过基架构 ...

  5. MySQL性能调优与架构设计——第 15 章 可扩展性设计之Cache与Search的利用

    第 15 章 可扩展性设计之Cache与Search的利用 前言: 前面章节部分所分析的可扩展架构方案,基本上都是围绕在数据库自身来进行的,这样是否会使我们在寻求扩展性之路的思维受到“禁锢”,无法更为 ...

  6. MySQL性能调优与架构设计——第 14 章 可扩展性设计之数据切分

    第 14 章 可扩展性设计之数据切分 前言 通过 MySQL Replication 功能所实现的扩展总是会受到数据库大小的限制,一旦数据库过于庞大,尤其是当写入过于频繁,很难由一台主机支撑的时候,我 ...

  7. MySQL性能调优与架构设计——第13章 可扩展性设计之 MySQL Replication

    第13章 可扩展性设计之 MySQL Replication 前言: MySQL Replication 是 MySQL 非常有特色的一个功能,他能够将一个 MySQL Server 的 Instan ...

  8. 第三章 web设计原则:

    程序员的修炼从优秀带卓越 第三章 web设计原则:    网站的评判标准     加载的速度要快     这到底是什么东西     给我看一个例子     清清楚楚的告诉我要做什么,并且扫除障碍   ...

  9. 数据库原理 第七章 数据库设计和ER模型

    第七章讲述一个E-R设计如何转换成一个关系模式的集合以及如何在该设计中找到某些约束. 1.概念设计定义了数据库中表示的实体.实体的属性.实体之间的联系,以及实体和联系上的约束 在设计一个数据库模型的时 ...

随机推荐

  1. spring使用之旅(二) ---- AOP的使用

    什么是AOP? AOP基本概念 AOP使用--注解方式 AOP使用--XML方式 实例--日志 写在最前面的(源码地址): https://github.com/xc83415134/spring_a ...

  2. Spring-Blog:个人博客(一)-Mybatis 读写分离

    概述: 2018,在平(tou)静(lan)了一段时间后,开始找点事情来做.这一次准备开发一个个人博客,在开发过程之中完善一下自己的技术.本系列博客只会提出一些比较有价值的技术思路,不会像写流水账一样 ...

  3. 创建对象-constructor丢失的问题

    fuction Person(name){ this.name=name; } Person.prototype={ sayName:function(){ return this.name; } } ...

  4. Java集合源码分析(四)HashMap

    一.HashMap简介 1.1.HashMap概述 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素 ...

  5. [bzoj3233] [Ahoi2013]找硬币

    一开始没什么思路...后来想到确定最大硬币面值就知道其他面值能取多少了..而且结果是可以由较小的面值转移过来的. f[i]表示最大面值为i时的最小硬币数.a[i]表示第i个物品的价钱. f[i]=mi ...

  6. javascript 对象-13

    对象 无序属性的集合,属性可以包含基本值.对象或者函数,简单理解为对象是若干属性的集合:我们常说的面向对象(oop)编程其实是指的一种编码的思想,简单理解为用对象来封装数据,利用封装.继承.多态对代码 ...

  7. CentOS6下安装git

    Ubuntu12.04中默认没有安装Git.需要自行安装. 1. 安装Git 1.1 Ubuntu12.04下 可以使用apt-get方式安装,也可以下载源代码安装[1],我们这里使用apt-git安 ...

  8. input框type=file设置cursor:pointer的问题

    为了让美化上传文件框,设置了cursor:pointer;,然而不起作用,然后百度找到了解决方法,设置font-size:0,这样就可以了.

  9. ASP.NET没有魔法——ASP.NET OAuth、jwt、OpenID Connect

    上一篇文章介绍了OAuth2.0以及如何使用.Net来实现基于OAuth的身份验证,本文是对上一篇文章的补充,主要是介绍OAuth与Jwt以及OpenID Connect之间的关系与区别. 本文主要内 ...

  10. [国嵌攻略][104][Linux内核模块设计]

    内核模块示例 #inlcude <linux/init.h> #inlcude <linux/module.h> static int hello_init(){ printk ...