您的位置:首页 > 编程语言 > C#

C# Language Specification 1.2 之六 转换

2006-08-03 16:05 232 查看

1. 转换

转换 (conversion) 使一种类型的表达式可以被视为另一种类型。转换可以是隐式的 (implicit) 或显式的 (explicit),这将确定是否需要显式地强制转换。例如,从 int 类型到 long 类型的转换是隐式的,因此 int 类型的表达式可隐式地按 long 类型处理。从 long 类型到 int 类型的反向转换是显式的,因此需要显式地强制转换。

int a = 123;
long b = a; // implicit conversion from int to long
int c = (int) b; // explicit conversion from long to int

某些转换由语言定义。程序也可以定义自己的转换(第 6.4 节)。

1.1 隐式转换

下列转换属于隐式转换:

· 标识转换

· 隐式数值转换

· 隐式枚举转换。

· 隐式引用转换

· 装箱转换

· 隐式常量表达式转换

· 用户定义的隐式转换

隐式转换可以在多种情况下发生,包括函数成员调用(第 7.4.3 节)、强制转换表达式(第 7.6.6 节)和赋值(第 7.13 节)。

预定义的隐式转换总是会成功,从来不会导致引发异常。正确设计的用户定义隐式转换同样应表现出这些特性。

1.1.1 标识转换

标识转换是在同一类型(可为任何类型)内进行转换。这种转换的存在,仅仅是为了使已具有所需类型的实体可被认为是可转换的(转换为该类型)。

1.1.2 隐式数值转换

隐式数值转换为:

· 从 sbyte 到 short、int、long、float、double 或 decimal。

· 从 byte 到 short、ushort、int、uint、long、ulong、float、double 或 decimal。

· 从 short 到 int、long、float、double 或 decimal。

· 从 ushort 到 int、uint、long、ulong、float、double 或 decimal。

· 从 int 到 long、float、double 或 decimal。

· 从 uint 到 long、ulong、float、double 或 decimal。

· 从 long 到 float、double 或 decimal。

· 从 ulong 到 float、double 或 decimal。

· 从 char 到 ushort、int、uint、long、ulong、float、double 或 decimal。

· 从 float 到 double。

从 int、uint、long 或 ulong 到 float 以及从 long 或 ulong 到 double 的转换可能导致精度损失,但决不会影响到它的数量级。其他的隐式数值转换决不会丢失任何信息。

不存在向 char 类型的隐式转换,因此其他整型的值不会自动转换为 char 类型。

1.1.3 隐式枚举转换

隐式枚举转换允许将 decimal-integer-literal 0 转换为任何 enum-type。

1.1.4 隐式引用转换

隐式引用转换为:

· 从任何 reference-type 到 object。

· 从任何 class-type S 到任何 class-type T(前提是 S 是从 T 派生的)。

· 从任何 class-type S 到任何 interface-type T(前提是 S 实现了 T)。

· 从任何 interface-type S 到任何 interface-type T(前提是 S 是从 T 派生的)。

· 从元素类型为 SE 的 array-type S 到元素类型为 TE 的 array-type T(前提是以下所列条件均为真):

o S 和 T 只是元素类型不同。换言之,S 和 T 具有相同的维数。

o SE 和 TE 都是 reference-type。

o 存在从 SE 到 TE 的隐式引用转换。

· 从任何 array-type 到 System.Array。

· 从任何 delegate-type 到 System.Delegate。

· 从 null 类型到任何 reference-type。

隐式引用转换是指一类 reference-type 之间的转换,这种转换总是可以成功,因此不需要在运行时进行任何检查。

引用转换无论是隐式的还是显式的,都不会更改被转换的对象的引用标识。换言之,虽然引用转换可能更改引用的类型,但决不会更改所引用对象的类型或值。

1.1.5 装箱转换

装箱转换允许任何 value-type 隐式转换为 object 或 System.ValueType 类型,或由该 value-type 实现的任何 interface-type。将 value-type 的一个值装箱包括以下操作:分配一个对象实例,然后将 value-type 的值复制到该实例中。结构可装箱为类型 System.ValueType,因为该类型是所有结构的基类(第 11.3.2 节)。

有关装箱转换的介绍详见第 4.3.1 节。

1.1.6 隐式常量表达式转换

隐式常量表达式转换允许进行以下转换:

· int 类型的 constant-expression(第 7.15 节)可以转换为 sbyte、byte、short、ushort、uint 或 ulong 类型(前提是 constant-expression 的值在目标类型的范围内)。

· long 类型的 constant-expression 可以转换为 ulong 类型(前提是 constant-expression 的值不为负)。

1.1.7 用户定义的隐式转换

用户定义的隐式转换由以下三部分组成:先是一个标准的隐式转换(可选);然后是执行用户定义的隐式转换运算符;最后是另一个标准的隐式转换(可选)。计算用户定义的转换的精确规则详见第 6.4.3 节中的说明。

1.2 显式转换

下列转换属于显式转换:

· 所有隐式转换。

· 显式数值转换。

· 显式枚举转换。

· 显式引用转换。

· 显式接口转换。

· 拆箱转换。

· 用户定义的显式转换。

显式转换可在强制转换表达式(第 7.6.6 节)中出现。

显式转换集包括所有隐式转换。这意味着允许使用冗余的强制转换表达式。

不是隐式转换的显式转换是这样的一类转换:它们不能保证总是成功,知道有可能丢失信息,变换前后的类型显著不同,以至值得使用显式表示法。

1.2.1 显式数值转换

显式数值转换是指从一个 numeric-type 到另一个 numeric-type 的转换,此转换不能用已知的隐式数值转换(第 6.1.2 节)实现,它包括:

· 从 sbyte 到 byte、ushort、uint、ulong 或 char。

· 从 byte 到 sbyte 和 char。

· 从 short 到 sbyte、byte、ushort、uint、ulong 或 char。

· 从 ushort 到 sbyte、byte、short 或 char。

· 从 int 到 sbyte、byte、short、ushort、uint、ulong 或 char。

· 从 uint 到 sbyte、byte、short、ushort、int 或 char。

· 从 long 到 sbyte、byte、short、ushort、int、uint、ulong 或 char。

· 从 ulong 到 sbyte、byte、short、ushort、int、uint、long 或 char。

· 从 char 到 sbyte、byte 或 short。

· 从 float 到 sbyte、byte、short、ushort、int、uint、long、ulong、char 或 decimal。

· 从 double 到 sbyte、byte、short、ushort、int、uint、long、ulong、char、float 或 decimal。

· 从 decimal 到 sbyte、byte、short、ushort、int、uint、long、ulong、char、float 或 double。

由于显式转换包括所有隐式和显式数值转换,因此总是可以使用强制转换表达式(第 7.6.6 节)从任何 numeric-type 转换为任何其他的 numeric-type。

显式数值转换有可能丢失信息或导致引发异常。显式数值转换按下面所述处理:

· 对于从一个整型到另一个整型的转换,处理取决于该转换发生时的溢出检查上下文(第 7.5.12 节):

o 在 checked 上下文中,如果源操作数的值在目标类型的范围内,转换就会成功,但如果源操作数的值在目标类型的范围外,则会引发 System.OverflowException。

o 在 unchecked 上下文中,转换总是会成功并按下面这样继续。

· 如果源类型大于目标类型,则截断源值(截去源值中容不下的最高有效位)。然后将结果视为目标类型的值。

· 如果源类型小于目标类型,则源值或按符号扩展或按零扩展,以使它的大小与目标类型相同。如果源类型是有符号的,则使用按符号扩展;如果源类型是无符号的,则使用按零扩展。然后将结果视为目标类型的值。

· 如果源类型的大小与目标类型相同,则源值被视为目标类型的值。

· 对于从 decimal 到整型的转换,源值向零舍入到最接近的整数值,该整数值成为转换的结果。如果转换得到的整数值不在目标类型的范围内,则会引发 System.OverflowException。

· 对于从 float 或 double 到整型的转换,处理取决于发生该转换时的溢出检查上下文(第
7.5.12 节):

o 在 checked 上下文中,如下所示进行转换:

· 如果操作数的值是 NaN 或无穷大,则引发 System.OverflowException。

· 否则,源操作数会向零舍入到最接近的整数值。如果该整数值处于目标类型的范围内,则该值就是转换的结果。

· 否则,引发 System.OverflowException。

o 在 unchecked 上下文中,转换总是会成功并按下面这样继续。

· 如果操作数的值是 NaN 或 infinite,则转换的结果是目标类型的一个未经指定的值。

· 否则,源操作数会向零舍入到最接近的整数值。如果该整数值处于目标类型的范围内,则该值就是转换的结果。

· 否则,转换的结果是目标类型的一个未经指定的值。

· 对于从 double 到 float 的转换,double 值舍入到最接近的 float 值。如果 double 值过小,无法表示为 float 值,则结果变成正零或负零。如果 double 值过大,无法表示为 float 值,则结果变成正无穷大或负无穷大。如果 double 值为NaN,则结果仍然是 NaN。

· 对于从 float 或 double 到 decimal 的转换,源值转换为用 decimal 形式来表示,并且在需要时,将它在第 28 位小数位数上舍入到最接近的数字(第 4.1.7 节)。如果源值过小,无法表示为 decimal,则结果变成零。如果源值为 NaN、无穷大或者太大而无法表示为 decimal,则将引发 System.OverflowException。

· 对于从 decimal 到 float 或 double 的转换,decimal 值舍入到最接近的 double 或 float 值。虽然这种转换可能会损失精度,但决不会导致引发异常。

1.2.2 显式枚举转换

显式枚举转换为:

· 从 sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double 或 decimal 到任何 enum-type。

· 从任何 enum-type 到 sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double 或 decimal。

· 从任何 enum-type 到任何其他 enum-type。

两种类型之间的显式枚举转换是通过将任何参与的 enum-type 都按该 enum-type 的基础类型处理,然后在产生的类型之间执行隐式或显式数值转换进行的。例如,给定具有 int 基础类型的 enum-type E,从 E 到 byte 的转换按从 int 到 byte 的显式数值转换(第 6.2.1 节)处理,而从 byte 到 E 的转换按从 byte 到 int 的隐式数值转换(第 6.1.2 节)处理。

1.2.3 显式引用转换

显式引用转换为:

· 从 object 到任何其他 reference-type。

· 从任何 class-type S 到任何 class-type T(前提是 S 为 T 的基类)。

· 从任何 class-type S 到任何 interface-type T(前提是 S 未密封并且 S 不实现 T)。

· 从任何 interface-type S 到任何 class-type T(前提是 T 未密封或 T 实现 S)。

· 从任何 interface-type S 到任何 interface-type T(前提是 S 不是从 T 派生的)。

· 从元素类型为 SE 的 array-type S 到元素类型为 TE 的 array-type T(前提是以下所列条件均为真):

o S 和 T 只是元素类型不同。换言之,S 和 T 具有相同的维数。

o SE 和 TE 都是 reference-type。

o 存在从 SE 到 TE 的显式引用转换。

· 从 System.Array 以及它实现的接口到任何 array-type。

· 从 System.Delegate 以及它实现的接口到任何 delegate-type。

显式引用转换是那些需要运行时检查以确保它们正确的引用类型之间的转换。

为了使显式引用转换在运行时成功,源操作数的值必须为 null,或源操作数所引用的对象的实际类型必须是一个可通过隐式引用转换(第 6.1.4 节)转换为目标类型的类型。如果显式引用转换失败,则将引发 System.InvalidCastException。

引用转换无论是隐式的还是显式的,都不会更改被转换的对象的引用标识。换言之,虽然引用转换可能更改引用的类型,但决不会更改所引用对象的类型或值。

1.2.4 拆箱转换

拆箱转换允许从类型 object 或 System.ValueType 到任何 value-type,或者从任何 interface-type 到实现了该 interface-type 的任何 value-type 进行显式转换。一个拆箱操作包括以下两个步骤:首先检查对象实例是否为给定 value-type 的一个装了箱的值,然后将该值从实例中复制出来。结构可以从类型 System.ValueType 进行拆箱,因为该类型是所有结构的基类(第 11.3.2 节)。

有关拆箱转换的进一步介绍详见第 4.3.2 节。

1.2.5 用户定义的显式转换

用户定义的显式转换由以下三个部分组成:先是一个标准的显式转换(可选),然后是执行用户定义的隐式或显式转换运算符,最后是另一个标准的显式转换(可选)。计算用户定义的转换的精确规则详见第 6.4.4 节中的说明。

1.3 标准转换

标准转换是那些预先定义的转换,它们可以作为用户定义转换的组成部分出现。

1.3.1 标准隐式转换

下列隐式转换属于标准隐式转换:

· 标识转换(第 6.1.1 节)

· 隐式数值转换(第 6.1.2 节)

· 隐式引用转换(第 6.1.4 节)

· 装箱转换(第 6.1.5 节)

· 隐式常量表达式转换(第 6.1.6 节)

标准隐式转换特别排除了用户定义的隐式转换。

1.3.2 标准显式转换

标准显式转换包括所有的标准隐式转换以及一个显式转换的子集,该子集是由那些与已知的标准隐式转换反向的转换组成的。换言之,如果存在一个从 A 类型到 B 类型的标准隐式转换,则一定存在与其对应的两个标准显式转换(一个是从 A 类型到 B 类型,另一个是从 B 类型到 A 类型)。

1.4 用户定义的转换

C# 允许通过用户定义的转换 (user-defined conversion) 来增加预定义的隐式和显式转换。用户定义的转换是通过在类类型和结构类型中声明转换运算符(第 10.9.3 节)引入的。

1.4.1 允许的用户定义转换

C# 只允许声明某些用户定义的转换。具体而言,不可能重新定义已存在的隐式或显式转换。仅当以下条件皆为真时,才允许类或结构声明从源类型 S 到目标类型 T 的转换:

· S 和 T 是不同的类型。

· S 和 T 中总有一个是声明了该运算符的类类型或结构类型。

· S 和 T 都不是 object或 interface-type。

· T 不是 S 的基类,S 也不是 T 的基类。

关于用户定义转换的限制在第 10.9.3 节中有进一步讨论。

1.4.2 用户定义的转换的计算

用户定义的转换将一个值从它所属的类型(称为源类型 (source type))转换为另一个类型(称为目标类型 (target type))。用户定义的转换的计算集中在查找符合特定的源类型和目标类型的最精确的 (most specific) 用户定义转换运算符。此确定过程分为几个步骤:

· 查找考虑从中使用用户定义的转换运算符的类和结构集。此集由源类型及其基类和目标类型及其基类组成(隐式假定只有类和结构可以声明用户定义的运算符,并且不属于类的类型不具有任何基类)。

· 由该类型集确定适用的用户定义转换运算符。一个转换运算符如满足下述条件就是适用的:必须可以通过执行标准转换(第 6.3 节)来使源类型转换为该运算符的操作数所要求的类型,并且必须可以通过执行标准转换来使运算符的结果类型转换为目标类型。

· 由适用的用户定义运算符集,明确地确定哪一个运算符是最精确的。概括地讲,最精确的运算符是操作数类型“最接近”源类型并且结果类型“最接近”目标类型的运算符。后面的章节定义了建立最精确的用户定义转换运算符的确切规则。

确定了最精确的用户定义转换运算符后,用户定义转换的实际执行包括三个步骤:

· 首先,如果需要,执行一个标准转换,将源类型转换为用户定义转换运算符的操作数所要求的类型。

· 然后,调用用户定义转换运算符以执行转换。

· 最后,如果需要,再执行一个标准转换,将用户定义转换运算符的结果类型转换为目标类型。

用户定义转换的计算从不涉及一个以上的用户定义转换运算符。换言之,从 S 类型到 T 类型的转换决不会首先执行从 S 到 X 的用户定义转换,然后执行从 X 到 T 的用户定义转换。

后面的章节给出了用户定义的隐式或显式转换的确切定义。这些定义使用下面的术语:

· 如果存在一个从 A 类型到 B 类型的标准隐式转换(第 6.3.1 节),并且 A 和 B 都不是 interface-type,则称 A 被 B 包含 (encompassed by)、称 B 包含 (encompass) A。

· 在一个类型集中,包含程度最大的类型 (most encompassing type) 是指这样的一个类型:它包含了该类型集中的所有其他类型。如果没有一个类型包含所有其他类型,则集中没有包含程度最大的类型。更直观地讲,包含程度最大的类型是集中“最大”的类型,即可将每个其他类型都隐式转换为该类型的一个类型。

· 在一个类型集中,被包含程度最大的类型 (most encompassed type) 是指这样一个类型:它被该类型集中的所有其他类型所包含。如果没有一个类型被所有其他类型包含,则集中没有被包含程度最大的类型。更直观地讲,被包含程度最大的类型是集中“最小的”类型,即可隐式转换为每个其他类型的一个类型。

1.4.3 用户定义的隐式转换

从 S 类型到 T 类型的用户定义的隐式转换按下面这样处理:

· 查找类型集 D,将从该类型集考虑用户定义的转换运算符。此集合由 S(如果 S 是类或结构)、S 的基类(如果 S 是类)和 T(如果 T 是类或结构)组成。

· 查找适用的用户定义转换运算符集合 U。集合 U 由用户定义的隐式转换运算符组成,这些运算符是在 D 中的类或结构内声明的,用于从包含 S 的类型转换为被 T 包含的类型。如果 U 为空,则转换未定义并且发生编译时错误。

· 在 U 中查找运算符的最精确的源类型 SX:

o 如果 U 中的所有运算符都从 S 转换,则 SX 为 S。

o 否则,SX 在 U 中运算符的合并源类型集中是包含程度最大的类型。如果找不到最直接包含的类型,则转换是不明确的,并且发生编译时错误。

· 在 U 中查找运算符的最精确的目标类型 TX:

o 如果 U 中的所有运算符都转换为 T,则 TX 为 T。

o 否则,TX 在 U 中运算符的合并目标类型集中是包含程度最大的类型。如果找不到这样的包含程度最大的类型,则转换是不明确的,并且发生编译时错误。

· 如果 U 中正好含有一个从 SX 转换到 TX 的用户定义转换运算符,则这就是最精确的转换运算符。如果不存在此类运算符,或者如果存在多个此类运算符,则转换是不明确的,并且发生编译时错误。否则,将应用用户定义的转换:

o 如果 S 不是 SX,则执行从 S 到 SX 的标准隐式转换。

o 调用最精确的用户定义转换运算符,以从 SX 转换到 TX。

o 如果 TX 不是 T,则执行从 TX 到 T 的标准隐式转换。

1.4.4 用户定义的显式转换

从 S 类型到 T 类型的用户定义的显式转换按下面这样处理:

· 查找类型集 D,将从该类型集考虑用户定义的转换运算符。该类型集由 S(如果 S 为类或结构)、S 的基类(如果 S 为类)、T(如果 T 为类或结构)和 T 的基类(如果 T 为类)组成。

· 查找适用的用户定义转换运算符集合 U。集合 U 由用户定义的隐式或显式转换运算符组成,这些运算符是在 D 中的类或结构内声明的,用于从包含 S 或被 S 包含的类型转换为包含 T 或被 T 包含的类型。如果 U 为空,则转换未定义并且发生编译时错误。

· 在 U 中查找运算符的最精确的源类型 SX:

o 如果 U 中的所有运算符都从 S 转换,则 SX 为 S。

o 否则,如果 U 中的所有运算符都从包含 S 的类型转换,则 SX 在这些运算符的合并源类型集中是被包含程度最大的类型。如果找不到最直接包含的类型,则转换是不明确的,并且发生编译时错误。

o 否则,SX 在 U 中运算符的合并源类型集中是包含程度最大的类型。如果找不到这样的包含程度最大的类型,则转换是不明确的,并且发生编译时错误。

· 在 U 中查找运算符的最精确的目标类型 TX:

o 如果 U 中的所有运算符都转换为 T,则 TX 为 T。

o 否则,如果 U 中的所有运算符都转换为被 T 包含的类型,则 TX 在这些运算符的合并源类型集中是包含程度最大的类型。如果找不到这样的包含程度最大的类型,则转换是不明确的,并且发生编译时错误。

o 否则,TX 在 U 中运算符的合并目标类型集中是被包含程度最大的类型。如果找不到最直接包含的类型,则转换是不明确的,并且发生编译时错误。

· 如果 U 中正好含有一个从 SX 转换到 TX 的用户定义转换运算符,则这就是最精确的转换运算符。如果不存在此类运算符,或者如果存在多个此类运算符,则转换是不明确的,并且发生编译时错误。否则,将应用用户定义的转换:

o 如果 S 不是 SX,则执行从 S 到 SX 的标准显式转换。

o 调用最精确的用户定义转换运算符,以从 SX 转换到 TX。

o 如果 TX 不是 T,则执行从 TX 到 T 的标准显式转换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: