类型设计规范

.NET设计规范————类型设计规范

类型设计规范

从CLR的角度看,只有值类型和引用类型两种类型,但是从框架设计的角度我们把类型从逻辑上分了更多的组。如下所示:

类是引用类型的一般情况,占了框架中的大多情况,类的流行归于它支持面向对象的特征,以及它的普遍的适用性,基类和抽象类是两个特殊的逻辑分组,它们与扩张性有关。

由于CLR不支持多继承,接口类型可以用来模拟多继承,既能被引用类型实现,也能被值类型实现。

结构是值类型的一般情况,应该用于小而简单的类型,就像编程语言的基本类型一样。

枚举是值类型的一个特例,它用来定义一小组值。

静态类是那些用来容纳静态成员的类型,常用来提供对其他操作的快速访问。

委托、异常、Attribute、数据、集合都是引用类型的特例,各有各自的用途。

1.1     类型和名字空间

在设计大型框架之前,应该决定如何将功能划分到一组功能域中,这些功能域由名字空间表示,为了确保一组有条理的名字空间包含的类型能很好的集成,不发生冲突,以及不会重复,自顶向下的设计很有必要。导致了下面的规范:

要用名字空间把类型组织成一个相关的特性域的层次结构。(主要是为了把类型组织成一个有条理、易于浏览的、易于理解的层次结构)

避免非常深的名字空间层次(难于浏览,需要经常回溯)

避免有太多的名字空间。

避免把为高级场景而设计的类型和常见的编程任务而设计的类型放在同一个名字空间中。(方便用户更容易理解框架的基本概念,容易在常见的场景中使用框架)

不要指定名字空间就定义类型。(把相关的类型组织到一个层次结构中,有助于解决可能存在的名字冲突)

标准子名字空间的命名

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

1.       .Design子名字空间

仅用于设计时的类型应该放在名为.Design的子名字空间。如:System.Windows.Forms.Design;

System.Messaging.Design;

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

2.       .Permissions子名字空间

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

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

3.       .Interop子命名空间

许多框架需要支持与旧系统的互操作性(interoperability)。

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

要用带“.Interop”后缀的名字空间来容纳所有位于PIA中的代码。

1.2     类和结构之间的选择

引用类型在堆上分配,由垃圾收集器管理;而值类型要么在栈上分配并在栈展开时释放,要么内联在容纳它的类型中并在容纳它的类型被释放时释放。因此,与引用类型的分配与释放相比,值类型的分配与释放开销更低。

引用类型的数组不是非内联分配的,意为数组元素只是一些引用,指向那些位于堆中的引用类型的实例。而值类型的分配是内联的,数组的元素就是值类型的真正实例。因此值类型的分配和释放的开销要比引用类型的大的多,在大多情况下,值类型数组具有更好的局部性。

值类型在被强制转换为对象或装箱因为装箱和对象是在堆上分配的,且由垃圾收集器管理,所以太多的装拆箱操作会对堆、垃圾收集器,并对系统性能造成影响。

引用类型的赋值是复制引用,而值类型的赋值复制整个值,对大的引用类型复制开销要比值类型小的多。

引用类型是引用传递,值类型是值传递。改变引用类型的一个实例会影响其他的实例,改变值类型的实例,不会影响到它的副本。

框架中的大多数类型应该是类,但是在某些特殊情况下,由于值类型所具有的特征,使用结构更合适。

考虑定义结构而不是类(如果该类型的实例比较小,生命周期比较短,经常被内嵌在其他对象中)

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

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

它的实例大小小于16字节

它是不可变的

它不需要被经常装箱

在所有的其他情况下,应该将类型定义为类。

1.3     类和接口之间的选择

一般来说,类是用来暴漏抽象的优先选择。

点在于当需要允许API不断演化时,它的灵活性不如类,一旦你发布了一个接口,它的成员就永远固定了,给接口添加任何东西都会破坏已经实现该接口的已有类型。

类提供了更多的灵活性,你可以给一个已发布的类添加成员。只要添加的方法不是抽象的,任何已有的派生类无需改变仍能继续使用。

要优先采用类而不是接口

与基于接口的API相比,基于类的API容易演化得多,因为可以给类型添加成员而不会破坏已有的代码。

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

抽象类经过正确的设计,同样能够解除协定与实现之间的耦合,与接口能达到的程度不相上下。

要定义接口,如果需要提供一个多态的值类型层次结构的话。(值类型不能自其它类型继承,但是她们可以实现接口)

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

1.4     抽象类的设计

不要在抽象类型中定义公有的或内部受保护的构造函数。

只有当用户需要创建一个类型的实例时,该类型的构造参数才是公有的,由于你无法创建一个抽象类的实例,因此如果抽象类型具有公有构造函数

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

受保护的构造函数仅仅是允许子类型被创建时,基类能够做自己的初始化。

内部构造函数可以用来把该抽象类的具体实现限制在定义该抽象类的程序集中。

要为发布的抽象类提供至少一个继承自该类的具体类型。

有助于验证该抽象类的设计是否正确。

1.5     静态类的设计

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

静态类是在纯面向对象设计和简单性之间的一个权衡,它们被广泛用来提供一下访问其他操作的快捷方式,或者不需要完整的面向对象封装器的时候提供一些功能。(System.Enviroment)

要尽量少用静态类

静态类仅被用作辅助类,来支持框架的面向对象的核心。

不要把静态类当做杂物箱。

每一个静态类都应该有其明确的目的。

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

要把静态类定义为密封的、抽象的,并添加一个私有的实例构造函数。

1.6     接口的设计

虽然大多数情况下API用类或结构来构建最好,但是在有些情况下,接口更合适。

CLR 不支持多继承,但允许类型实现一个或多个接口,因此通常用接口来实现多继承。

在创建能够为多种类型(包括值类型)所支持的公共接口时。

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

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

避免使用记号接口(没有成员的接口)

要为接口提供至少一个实现该接口的类型。

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

不要给已发行的接口再添加成员。

这样做会破坏该接口的实现,为了避免版本的问题,应该创建一个新的接口。

一般来说,在为托管代码设计可重用的程序库时,你应该选择类而不是接口。

1.7     结构的设计

通用目的的值类型通常称为struct(结构)。

不要为结构提供默认的构造函数。(C#不允许结构有默认的构造函数)

要确保所有的实例数据都为零,false,或null时,结构仍处于有效状态。(可以防止在创建一个结构时创建出无效的实例)

要为值类型实现IEquatable<T>

值类型的Object.Equals方法会导致装箱,默认的实现并不高效,因为使用了反射,IEquatable<T>.Equals性能好的多,不会导致装箱。

不要显示的扩展System.ValueType

1.8     枚举的设计

枚举是一种特殊的值类型,有两种类型的枚举:简单枚举和标记枚举。

简单枚举代表小型的、闭合的一组选择。例如(一组颜色):

Public enumColor{

Red,

Green,

Blue,

……

}

标记枚举的设计是为了支持对枚举值进行按位操作。标记枚举的常见例子是一个选择列表

[Flags]

Public enumAttributeTargets

{

Assembly=0x0001,

Module=0x0002,

Cass=0x0004,

Struct=0x0008

}

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

要优先使用枚举而不要使用静态常量。(枚举是一个包含一组静态常量的结构)

不要把枚举用于开发的集合(比如操作系统版本等)

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

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

不要把sentinel值包含在枚举值中。

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

考虑用Int32作为枚举的基本实现类型。

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

不要直接扩充System.Enum

1.8.1  标记枚举的设计

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

[Flags]

Public enumAttributeTargets

{

…..

}

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

[Flags]

Public enumWatcherChangeTypes

{

Created=0x0002,

Deleted=0x0004,

Changed=0x0008,

Renamed=0x00010,

}

考虑为常用的标记组合提供特殊的枚举值。(位操作是一个高级概念,对应简单任务来说不是必须的,FileAccess.ReadWrite就是这样一个例子)

[Flags]

Public enumFileAccess

{

Read=1,

Write=2,

ReadWrite=Read|Write,

}

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

避免把零用作标记枚举的值,除非该值表示“所有标记都被清除“,而且按下一条规范进行了适当的命名。(CLR规定任何值类型的默认值“所有的位都清零“)

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

[Flags]

Public enumBorderStyle

{

Fixed3D=0x1,

FixedSingle=0x2,

None=0x0

}

但是,该规则只适用于标记枚举,对于非标记枚举的情况,避免使用0值实际上是不利的,所有的枚举类型一开始都为零值。

1.8.2  给枚举添加值

常会发现在发现之后需要给一个枚举添加值,如果新添加的值是一个已有API的返回值,那么就存在潜在的应用程序兼容性问题。

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

如果有实际数据,表明给枚举添加值会导致应用程序的不兼容,可以考虑添加一个新的API来返回新老枚举值,这样就能确保仍然兼容现有的应用程序。

1.9     嵌套类型

嵌套类型是一个定义在另一个类型的作用域内的类型。另一个类型被称为外层类型。嵌套类型能够访问外层类型的所有成员。可以访问定义在外层类型的私有字段以及定义在外层类型的所有父类的受保护字段。

一般来说,尽量少用嵌套类型,嵌套类型与外层类型紧密耦合,不适合将它们作为通用类型。嵌套类型适合用来对它们的外层类型的实现细节建模。

要想让一个类型能够访问外层类型的成员时才使用嵌套类型。

不要用嵌套类型进行逻辑分组,应该用名字空间来达到此目的。

避免公开的暴漏嵌套类型,唯一的例外是如果只需要在极少数的场景中声明嵌套类型的变量,比如派生子类,或者其他高级自定义场景中。

不要使用嵌套类型,如果该类型可能会被除了它的外层类型之外的类型引用。

不要使用嵌套类型,如果它们需要被客户代码实例化。

不要把嵌套类型定义为接口的成员。

一般来说尽量少用嵌套类型,而且应该避免将嵌套类型公开暴漏给外界。

时间: 2024-10-06 16:27:31

类型设计规范的相关文章

《.NET 设计规范》第 4 章:类型设计规范

第 4 章:类型设计规范 4.1 类型和命名空间 要用命名空间把类型组织成一个由相关的功能区所构成的层次结构中. 避免非常深的命名空间层次.因为用户需要经常回找,所以这样的层次浏览起来很困难. 避免有太多的命名空间. 避免把为高级方案而设计的类型和为常见编程任务而设计的类型放在同一个命名空间中. 不要不指定类型的命名空间就定义类型. 要把那些为基本命名空间提供设计时功能的类型放在带“.Design”后缀的命名空间中. 要把那些为基本命名空间提供自定义权限的类型放在带“.Permissions”后

.NET设计规范————类型设计规范

类型设计规范 从CLR的角度看,只有值类型和引用类型两种类型,但是从框架设计的角度我们把类型从逻辑上分了更多的组.如下所示: 类是引用类型的一般情况,占了框架中的大多情况,类的流行归于它支持面向对象的特征,以及它的普遍的适用性,基类和抽象类是两个特殊的逻辑分组,它们与扩张性有关. 由于CLR不支持多继承,接口类型可以用来模拟多继承,既能被引用类型实现,也能被值类型实现. 结构是值类型的一般情况,应该用于小而简单的类型,就像编程语言的基本类型一样. 枚举是值类型的一个特例,它用来定义一小组值. 静

持续集成方案

大纲 构建 版本控制 部署 单元测试 架构文档化 命名约定 数据库伸缩性 自动化 反馈 实践 引言: 持续集成的前身: 在使用持续集成之前,很多开发团队都是用每日构建(nightly build).当时,微软使用这个实践很多年了.谁破坏了构建,就要负责监视后续的构建构成,直至发现下一个破坏了构建的人. 为什么要使用持续集成? 对于大多数项目来说,采纳持续集成实践是向高效率和高质量迈进的一大步.它保证那些创建大型复杂系统的团队具有高度的自信心和控制力.一旦代码提交引入了问题,持续集成就能为我们提供

[转载]C#编程规范

C#编程规范 2011-06-24 13:44:55|  分类: 雪球|举报|字号 订阅 1. 简介 本规范为一套编写高效可靠的 C# 代码的标准.约定和指南.它以安全可靠的软件工程原则为基础,使代码易于理解.维护和增强,提高生产效率.同时,将带来更大的一致性,使软件开发团队的效率明显提高. 2. 适用范围 本规范适用于公司所有的C#源代码,为详细设计,代码编写和代码审核提供参考和依据. 3. 文体 本规范中的建议分为四种:要,建议,避免,不要,表示需要遵循的级别.文档中会以粗体表示.对于应遵循

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

http://www.cnblogs.com/yangcaogui/archive/2012/04/20/2459567.html 接着 → .NET设计规范一:设计规范基础 上一篇,我们来了解下类型成员命名的设计! 3.类型成员命名的设计 3.1字段 ①遵循“ camelCasing  ”的命名规则 ②要用名词或名词词组,不要使用C#关键字 ③不要给字段添加任何前缀 ④定义常量的时候要使用“PascalCasing ”的命名规范 ⑤当定义私有变量的时候使用“camelCasing”命名,并且在

MYSQL数据库设计规范与原则

MYSQL数据库设计规范 1.数据库命名规范 采用26个英文字母(区分大小写)和0-9的自然数(经常不需要)加上下划线'_'组成; 命名简洁明确(长度不能超过30个字符); 例如:user, stat, log, 也可以wifi_user, wifi_stat, wifi_log给数据库加个前缀; 除非是备份数据库可以加0-9的自然数:user_db_20151210; 2.数据库表名命名规范 采用26个英文字母(区分大小写)和0-9的自然数(经常不需要)加上下划线'_'组成; 命名简洁明确,多

MySQL设计规范与性能优化

引言 MySQL是目前使用最为广泛的关系型数据库之一,如果使用得当,可支撑企业级高并发.高可靠服务,使用不当甚至连并发量略高的个人网站都难以支撑: 就算使用了缓存,大量的数据库访问依旧在所难免,即使设置了较长的缓存有效期,而且缓存命中率较理想,但缓存的创建和过期后的重建都是需要访问数据库的: 本文主要从MySQL表结构设计规范和MySQL自身性能优化两方面来讨论该如何对MySQL数据库进行优化: MySQL表结构设计规范 1. 数据库设计命名规范 (1)数据库,数据表一律使用前缀,前缀名称一般不

团队编程项目作业2-团队编程项目代码设计规范

代码设计规范 其中有一些强制性的重要的原则:首先就是字符串的拼加操作,必须使用StringBuilder,其次就是关于try-catch的一些用法 1.基本原则 1.对于捕获后,不知道干什么事情或者也不知道怎样处理的情况,就不要捕获异常,留给外出层去捕获处理:     2.返回类型为集合的,在方法声明中必须使用泛型,必须在javadoc中注明什么情况下返回null,什么情况下返回空集合.     3.对于方法.变量声明范围要采用如下优先级:private.protected.public,对于变

设计规范,你怎么支持?

这周我们部门的设计团队打算制定一套设计规范,目的是约束全站页面的设计统一性,今后对于一些通用需求和基础交互有一个统一的依据.以免不同的设计师在同一个大项目中设计出两种不同的风格,比如字体.颜色.间距等通用标准. 设计规范和前端的关系 当我听到这个消息后,我想到的是前端也应该提早介入这件事.举个例子,当前端同学切页面的时候,是不是要把设计稿中的字体写到每一级的css中,如果不写完全依赖字体继承,就会带来很大的不确定性,比较危险.但是如果写,写到什么程度,要不要每一级都写还是挑几个写.但是一但写多了