ECMA-335(CLI)标准 读书笔记(第一部:概念和架构 第8章~8.2)

 

上一篇:ECMA-335(CLI)标准 读书笔记(第一部:概念和架构 第7章) 

 

8.       Common Type System

类型描述了值并指定了该类型的所有值应该支持的契约(见8.6章)。因为CTS既支持面向对象编程(OOP)语言,也支持函数式和过程式编程语言,所以它涉及到两种实体:对象和值。值是简单的位模式,如整型和浮点型;每个值都有个类型来描述它所占用的存储空间和其呈现中位的意义,也能描述在呈现上所能做的操作。值用于代表诸如C编程语言中相应的简单类型,也要代表如C + +Java™语言中非对象的东西。

对象比值可以做更多。每个对象都是自描述类型的,也就是说,它的类型是明确保存在其呈现里的。它有区别于其它所有对象的特性,并有位置存储其它的实体(这可能是对象或者值)。当内部位置上的内容被改变时,对象的特性不会变。

有几种对象和值,如下图资料显示。

   

 

注:托管指针可以指向堆中。

 

泛型特征允许用一种模式来定义一整套的类型和方法,其中包括叫做泛型参数的占位符。这些泛型参数需要通过特定类型被替换,实例化为该家族成员的实际需要。泛型的设计符合下列目标:

  • 相关性:泛型类型可以出现在任何CLI类型存在的地方。
  • 语言独立:不对源语言做出任何假设。但是,CLI的泛型试图支持尽可能多语言的现有类中的泛型功能。此外,设计允许对当前缺乏泛型的语言进行新扩展。
  • 实现独立:一个CLI的实现被允许专门逐一实现呈现和代码,或可能通过装箱和拆箱值来共享所有的呈现和代码。
  • 实现效率:泛型的性能并不比用对象仿真泛型的效率要差。一个好的实现要做得更好,就要避免在引用类型的实例化方面含糊不清,要对值类型的实例化产生专门的代码。
  • 对定义做静态检查:泛型定义能被独立于实现来验证核实。这样,泛型类型被静态验证,它的方法对所有有效的实例化而言向JIT编译提供了保障。
  • 考虑泛型参数的统一行为:总的来说,参数化的类型和泛型方法的行为在所有类型的实例化上是“相同的”。

另外,CLI支持协变和逆变泛型参数,带有下列特征:

  • 类型安全(基于纯静态检查)
  • 简单:特殊情况下的,不一致仅允许存在于泛型接口和泛型代理上(非类或值类型)
  • 不希望支持差异的语言可以忽略这些特征,并将所有的泛型类型都作为一致的。
  • 能够实现某些语言中更复杂的协作方案,如Eiffel

8.1   面向对象编程的关系

术语类型(Type)经常被用于面向值编程的领域里来表示数据的呈现。在面向对象领域里经常指行为而不是呈现。在CTS中,类型被用于表达这两种意思:当且仅当两个实体有兼容的呈现和兼容的行为时,它们才有兼容的类型。这样,在CTS中,一个类型派生自一个基类型,那么派生类型的实例能被基类型的实例替代,因为两者的呈现和行为是兼容的。

与一些OOP语言不同,在CTS中,两个具有从根本上不同呈现的对象有不同的类型。一些OOP语言用了一个类型上的不同概念。它们认为如果两个对象以同样的方式响应同一套消息,那么它们就有相同的类型。这个概念在CTS中被这种说法取代了,即对象实现相同的接口。

同样的,一些OOP语言(如Smalltalk)认为消息传递是计算的基本模式。在CTS中,这相当于调用虚方法(见8.4.4章),这里虚方法的签名扮演着消息的角色。

CTS自身并不使用“无类型编程”的概念。也就是说,没有办法在不知道对象类型的情况下调用非静态的方法。尽管如此,无类型编程仍能在基于实现了反射的包(见第四部)提供的工具的基础上实现。

8.2   值和类型

类型描述值。任何被类型描述的值都是类型的一个实例。任何值的使用——存储它,作为参数传递它,操作它——都要求一个类型。这特别应用于所有的变量、参数、栈单元的赋值和方法结果上。类型定义了容许值和被类型的值支持的容许的操作。所有的操作符和函数应该具有每个可访问或使用的值的类型。

每个值都有一个确切的值来完整描述它的类型属性。

每个值是它的确切类型的一个实例,也能是其它类型的一个实例。特别是,如果一个值是一个派生自其它类型的一个类型的实例,那么它也是另外那个类型的实例。

8.2.1          值类型和引用类型

有两种类型:值类型和引用类型。

  • 值类型——被一个值类型描述的值是自包含的(每个值都能在不引用其它值的情况下被理解)。
  • 引用类型——被引用类型描述的值中记录了另一个值的位置

有四种引用类型:

·         对象类型是一个自描述值的引用类型(见8.2.3章)。一些对象类型(如抽象类型)仅是一个值的部分描述。

·         接口类型总是一个值的部分描述,被许多对象类型潜在地支持。

·         指针类型是一个编译时的值描述,这些值的呈现是本地的一个机器地址。

·         内建的引用类型。

8.2.2          内建的值和引用类型

下面的数据类型是一个CTS的整体部分,被VES直接支持。它们在持久的metadata中有特殊的编码。

1:特殊的编码

 

                                                                                           Table 1: Special Encoding

Name in CIL assembler

(see Partition II)

CLS Type?

Name in class library

(see Partition IV)

Description

bool1

Yes

System.Boolean

True/false value

char1

Yes

System.Char

Unicode 16-bit char.

object

Yes

System.Object

Object or boxed value type

string

Yes

System.String

Unicode string

float32

Yes

System.Single

JEC 60559:1989 32-bit float

float64

Yes

System.Double

JEC 60559:1989 64-bit float

int8

No

System.SByte

Signed8-bitinteger

int16

Yes

System.Int16

Signed 16-bit integer

int32

Yes

System.Int32

Signed32-bitinteger

int64

Yes

System.Int64

Signed64-bitinteger

native int

Yes

System.IntPtr

Signed integer, native size

native unsigned int

No

System.UIntPtr

Unsigned integer, native size

typedref

No

System.TypedReference

Pointer plus exact type

unsigned int8

Yes

System.Byte

Unsigned 8-bit integer

unsigned int16

No

System.UInt16

Unsignedl6-bitinteger

unsigned int32

No

System.UInt32

Unsigned32-bitinteger

unsigned int64

No

System.UInt64

Unsigned64-bitinteger

 

在上表中归类的boolchar 类型为整型。

8.2.3          类、接口和对象

一个类型如果明确得定义了一个值的呈现和定义在值上的操作,那么它就完整得描述了一个值。

对于一个值类型,定义呈现需要描述组成值呈现的位的次序。对一个引用类型而言,定义呈现需要描述组成值呈现的位的位置和次序。

方法描述了能在一个确切类型的值上所做的操作。定义一套在确切类型的值上的操作需要为每个操作命名方法。

一些类型仅是部分描述;例如接口类型。这些类型描述了一套操作的子集,没有呈现,因此不是任何一个值的确切呈现。因此,当一个值有一个确切类型的时候,它也可以是许多类型的一个值。进一步说,因为确切类型完整描述了一个值,它也完全指定了一个确切的值所能拥有的所有其它类型。

当每个值都有个确切类型的时候,不总是能通过查看值的呈现来决定它的确切类型。特别,永远不可能决定一个值类型的值的确切类型。考虑两个内建的值类型,32位的有符号和无符号整数。当每个类型是它们各自值的完整说明时(如确切的类型),没有办法从一个值的专有的32位序列中得到确切的类型。

对于一些叫做对象的值,总是可能从值中获得确切的类型。对象的确切类型也叫做对象类型。对象是引用类型的值,但并不是所有引用类型描述对象。考虑一个指向32位整型的指针的值,一种引用类型。没有办法通过检查位信息来获取其值类型;因此它不是个对象。现在考虑内建的CTS引用类型System.String(见第四部分)。这个类型的值的确切类型总是通过检查值来确定,因此类型System.String的值是对象,System.String是对象类型。

8.2.4          值的装箱和拆箱

对每个值类型来说,CTS定义了一个统一的引用类型叫做装箱类型(boxed type)。反之是不成立的:一般而言,引用类型没有一个统一的值类型。一个装箱类型的值的呈现(装箱值)位于值类型的值被存储的地方。一个装箱类型是一个对象类型,装箱值是一个对象。

一个装箱类型不能通过名字被直接引用,因此没有字段或者本地变量能被给定一个装箱类型。最接近装箱枚举值类型的指定基类是System.Enum;对于所有其它的值类型是System.ValueType。类型为System.ValueType的字段仅能包含空值或者一个装箱值类型的实例。类型为System.Enum的本地变量仅能包含空值或者一个装箱枚举类型的实例。

所有的值类型有一个叫做装箱(box)的操作。装箱一个任意值类型的值产生它的装箱值;例如,一个统一的装箱类型的值包含了原值的按位拷贝。如果值类型是空类型——定义为值类型System.Nullable<T>的一个实例——结果是一个空引用或者类型T的值属性的按位拷贝,依赖于其HasValue属性(分别是falsetrue)。所有的装箱类型有个拆箱(unbox)操作,这就出现了一个指向值的位呈现的托管指针。

装箱指令不仅只能用于值类型;这样的类型被叫做可装箱(boxable)类型。如果类型是下面的一种情况那就是可装箱的:

  • 一个不包含能指向CIL运算栈字段的值类型(包括泛型值类型的实例)。

【理由:一个值类型上有些字段不能被装箱,否则那些指向CIL运算栈顶的嵌入指针可能存在更长的时间。例如:System.RuntimeArgumentHandle, System.TypedReference。包含这样指针的值类型非正式的被描述为“按引用式(byref-like)”的值类型。】

  • 一个引用类型(包括类、数组、代理和泛型类的实例化)
  • 一个非托管的指针类型
  • 一个泛型参数(对一个泛型类型定义或泛型方法定义)【注:泛型参数的装箱和拆箱增加了CLI执行的性能开销。constrained前缀能够在实际调度值类型定义的方法时提高性能,这是通过避免装箱值类型做到的。

类型System.Void永远是不可装箱的。

接口和继承只定义在引用类型上。这样,当值类型定义(见8.9.7章)时能同时指定应该被值类型实现的接口和它继承自的类(System.ValueTypeSystem.Enum),这仅用于装箱值。

CLS 规则3:装箱的值类型不是CLS兼容的

【适当的时候用System .ObjectSystem.ValueTypeSystem.Enum替代装箱类型】

CLS (consumer):不需要导入装箱值类型

CLS (extender):不需要提供语法规则来定义或使用装箱值类型

CLS (framework):不应该将装箱值类型用在它的公共导出方面。

8.2.5          值的相同和相等

有两个定义在所有值对上的二元运算符:相同(identity)和相等(equality)。它们返回Boolean结果,是数学运算符上的等于。也就是说,它们是:

  • 反身的 – a op a true
  • 相称的当且仅当b op a true时,a op btrue
  • 可传递的如果a op btrue并且b op ctrue,那么 a op ctrue

另外,虽然相同总是意味着相等,反之却不成立。为理解这些操作之间的差异,考虑3个变量ABC,它们的类型是System.String,这里箭头的含义是“是一个对的引用”:

如果字符序列的位置是相同的(也就是说事实上只有一个字符串在内存里),这些变量的值是完全相同的。如果存储的字符序列是相同的,存储在变量里的值是相等的。这样,变量AB的值是相同的,变量AC以及BC各不相同,所有3ABC的值是相等的。

8.2.5.1    相同

相同操作符被CTS如下定义:

  • 如果值有不同的确切类型,那么它们是不相同的。
  • 除此以外,如果它们的确切类型是值类型,那么当且仅当值的位序列,位到位都是相同的,它们就是相同的。
  • 除此以外,如果它们的确切类型是引用类型,那么当且仅当值的位置是相同的,它们就是相同的。

相同在System.Object通过ReferenceEquals方法来实现。

8.2.5.2    相等

对于值类型,相等运算符是确切的值类型定义的一部分。相等的定义应该服从以下规则:

  • 相等应该是一个等价操作符,如上面所定义的。
  • 相同应该意味着相等,如前面所述。
  • 如果其中一个(或者两个)操作数是装箱类型,相等的判断应该通过以下方式:
    • 首先拆箱所有的装箱操作数,然后
    • 对结果的值做常用的是否相等的判断。

相同在System.Object通过Equals方法来实现。

【注:尽管两个浮点型指针NaNIEC 60559:1989定义为比较总是不同,System.Object.Equals的契约要求重载必须满足一个相等运算符的要求。因此,当比较两个NaN时,System.Double.EqualsSystem.Single.Equals返回true,而这种情况下如按IEC标准,相等运算符应返回False

 

 

版权声明作者:Cubean
出 处:http://www.cnblogs.com/cubean/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且 在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

posted @ 2010-03-19 00:26  cubean  阅读(1694)  评论(0编辑  收藏  举报