作者:思多雅[天行健] 发布时间:2008.12.01
上一章节,我们一起学习了C#的变量,这一章,我们来一起学习C#的转换。
C# 允许程序员在类或结构上声明转换,以便可以使类或结构与其他类或结构或者基本类型相互进行转换。转换的定义方法类似于运算符,并根据它们所转换到的类型命名。
C#的转换,又分为:1、隐式转换,2、显式转换,3、标准转换,4、用户自定义转换;下面,我们和大家一起学习:
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
一、隐式转换
下面的这几种类型的转换被称之为隐式转换
同一性转换
隐式数值转换
隐式枚举转换
隐式引用转换
包装转换
隐式常数表达式转换
用户自定义隐式转换
隐式转换可以在很多种情况下发生,包括函数子句调用,cast 计算表达式和赋值语句。
预定义的隐式转换总会成功而且不会引发异常,适当合理的用户自定义隐式转换也可以展示出这些特性。
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 到float 以及从long 到double 类型的转换可能会造成精度的损失,但并不会造成数量上的损失。除此之外的其他隐式数值转换不会损失任何信息。这里不存在转到char 类型的隐式数值转换,也就是说其他的整型数据不会被自动地转换为字符型数据。
1.3 隐式枚举转换
一个隐式枚举转换允许小数-整数实字(decimal-integer -literal)被转换成任意的枚举类型。
1.4 隐式引用转换
隐式reference 转换可以在下面类型之间发生:
从任意引用类型到对象。
从任意类类型S到任意类类型T, 只要S是由T 派生出来的。
从任意类类型S到任意接口类型T, 只要S实现T。
从任意接口类型S到任意接口类型T, 只要S是由T 派生出来的。
* 从一个带有元素类型S 的数组类型S到一个带有元素类型T 的数组类型T,只要下述各项均成立
S和T 只是元素类型不同。换句话说,S和T 有相同的维度。 S 和T 都是引用类型。
存在从S 到T 隐式引用转换。
从任意数组类型到System.Array。
从任意代表类型到System.Delegate。
从任意数组类型或代表类型到System.Icloneable。
从null 类型到任意引用类型。.
隐式引用转换指的是在引用类型间肯定可以成功的类型转换,它们是不需要实时检测的。
引用转换,不管是显式或是隐式的都不会改变被转换对象的引用一致性。换句话说,当引用转换发生时,它并未改变被转换对象的数值。
1.5 转换
包装转换允许任何数值类型被隐式地转换为类型对象,或者任何由这个数值类型实现的接口类型。
包装一个数值类型的数值包括分配一个对象实例并且将数值类型的数值复制到实例当中。
1.6 隐式常数表达式转换
隐式常数表达式转换允许下列类型的转换:
整型常数表达式可以被转换成为sbyte,byte,short,ushort,uint 或者ulong 类型,只有这个整型常数表达式的数值未超出目标类型的取值范围就行。
一个long 类型的常数表达式可以被转换成为ulong 类型,只有这个常数表达式的取值非负就行。
1.7 用户自定义隐式转换
一个用户自定义转换包括可选的标准隐式转换,紧随其后的是一个用户自定义的隐式转换运算符,之后是另一个可选的标准隐式转换。在§6.4.3 节中我们将详尽地描述用户自定义转换的取值规则。
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
二、显式转换
下列各种类型的转换属于显式转换:
所有隐式转换
显式数值转换
显式枚举转换
显式引用转换
显式引用转换
解包转换
用户自定义显式转换
显式转换可以在cast 表达式中出现。
显式转换并不总是能够成功,比如那些已知的损失信息的转换和那些足以使用显式说明的不同类型之间的转换。显式转换包括所有隐式转换,这就意味允许冗余的cast 表达式。
2.1 显式数值转换
显式数值转换指的是那些从一种数字类型到另外一种数字类型之间不能通过隐式类型转换实现的转换包括如下类型:
从sbyte 到byte,ushort,uint,ulong 或char 类型的转换。
从byte 到sbyte 和char 类型的转换。
从short 到sbyte,byteushort,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,shortushort,int,uint,long,ulong,char,float 或double 类型的转换。
由于形式类型转换包括所有隐式类型转换和显式数值转换,所以往往会出现使用cast 表达式完成从一种数字类型到另外一种数字类型的转换。
显式数值转换有可能造成自身信息损失或者异常。一个显式的数值转换是通过下述方式进行的:
对于从一种整型到另外一种整型的转换,具体过程取决于溢出检测的包含转换操的程序段: 在检测完成的程序段,如果源操作数未超出目标类型数据的取值范围,转换可以顺利完成;但是如果源操作数超出目标类型数据的取值范围,转换将生成一个溢出异常。在未完成检测的程序段,转换总是可以顺利完成,但是源操作数的最高位会被略去。
对于从float,double 或decimal 类型到整型的转换,源操作数是接近0 的整数,此值就是转换的结果。假如转换得到的整数超出目标类型允许的数值范围,转换将产生一个溢出异常。
对于从双精度到浮点数类型的转换,此双精度数值会逼近最为接近的浮点数数值。假如此双精度数太小无法使用浮点数表示,那么转换的结果将是0+或0-。假如此双精度数太大无法使用浮点数表示,那么转换结果将是-∞或+∞。假如此双精度数为NaN,那么转换的结果也是NaN 。
对于从浮点数或双精度到十进制类型的转换,源操作数会被转换成相应的十进制数,而当此十进制数达到或超过28 位长时,转换结果将是数值最接近的某一个十进制数。假如此源操作数太小无法用十进制数表示时,转换的结果将时0。当源操作数位NaN,∞或者超出十进制数的表示范围时,转换将产生一个InvalidCast 异常。
对于从十进制数到浮点数或双精度类型的转换,此十进制数会被转换为数值上最接近的浮点数或双精度数。虽然此转换会损失精度,但并不会造成异常。
2.2 显式枚举类型转换
显式枚举类型转换是指:
从sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double 或十进制到任意枚举类型的转换;
从任意枚举类型到sbyte,byte,short,ushort,int,uint,logn,ulong,char,floart,double 或十进制类型的转换;
从某一种枚举类型到另外一种枚举类型的转换 。
显式枚举类型转换是通过把源枚举类型看作此类型的原始类型,之后进行相应类型之间的显式或隐式类型转换。例如,如果有一个枚举类型E 且原始类型为int,那么从E 到byte 的转换将被作为一个显示的从int 到byte 的数字转换处理,而从byte 到的E 转换将被作为一个显示的从byte 到int的数字转换处理。
2.3 显式引用类型转换
显式引用类型转换指:
从对象(object)到任意引用类型之间的转换
从任意类类型S 到任意类类型T 之间的转换,其中S 是T 的基类。
从任意类类型 S 到任意类类型T 之间的转换,其中S 没有被封装,并且S 没有实现T。
从任意接口类型 S 到任意类类型T 之间的转换,其中T 没有被封装,并且T 没有实现S。
从任意接口类型S 到任意接口类型之间的转换,其中S 不是从T 派生的。
从任意具有元素SE 的数组类型S 到任意具有元素 TE 的数组类型T 之间的转换,只有满足下列条件才能进行:
S 和T 只在数字元素类型上存在不同。换句话说,S 和T 维数相同。
不论SE 或T E 都必须为引用类型。
从SE 或T E 之间的转换为显式引用转换。
从System.Array 到任意数组类型之间的转换。
从System.Delegate 到任意代表类型之间的转换。
从System.Icloneable 到任意数组类型或者代表类型之间的转换。
显式引用转换需要实时检测保证其正确进行的引用类型之间的转换。
对于一个null 类型或由源变量引用的实际的对象类型,必须是能够被隐式的引用转换来转换为目标类型的类型。假如显式引用转换失败,转换会生成一个InvalidCast 异常。
引用转换,无论是显式的或隐式的,并不改变被转换对象的引用一致性。换句话说,当一个引用转换改变数值的类型时,并未改变数值本身。
2.4 解包转换
解包转换允许从类型对象到任意数值类型,或者从任意接口类型到任意实现该接口类型的数值类型间的转换。解包操作包括首先检测对象实例为给定数值类型包装的数值,之后从那个实例中复制出数值。解包转换将在节中进一步说明。
2.5 用户自定义显式转换
用户自定义显式转换包括可选的标准显式转换,紧随其后的是一个用户自定义的显式转换运算符,之后是另一个可选的标准显式转换。在§6.4.4 节中我们将详尽地描述用户自定义转换的取值规则。
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
三、标准转换
标准转换指那些可以在用户自定义转换中出现的预定义的转换。
3.1 标准隐式转换
下列隐式转换属于标准隐式转换:
同一性类型转换
隐式数值类型转换
隐式引用转换
包装转换
隐式常数表达式转换
标准隐式转换是对立于用户自定义隐式转换而言的。
3.2 标准显式转换
标准显式转换包括所有标准隐式转换以及显式转换的子集,这是标准的隐式转换存在的原因。换句话说,假如存在一个从类型A 到类型B 的标准隐式转换,那么必然存在这两种类型相互之间的标准显式转换。
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
四、用户定义转换
C #允许可以被用户自定义转换调用的预定义显式和隐式转换。用户自定义转换是通过类和结构体中出现的转换运算符引入的。
4.1 允许的用户自定义转换
C #只允许某些用户自定义转换被声明。而且,它不允许重新定义一个已经存在的显式或者隐式转换。当所有下列的各项条件都满足时,类或结构体才被允许声明一个从源类型S 到目标类型T 的转换:
S 和T 是不同的类型
S 或者T 必须是包含转换操作符声明的类或结构类型。
S 或者T 是对象或一个接口类型。
T 不是S 的父类,S 也不是T 的父类。
4.2 用户自定义转换的取值
用户自定义转换将称作源类型的数值转换为另外一种称作目标类型的数据类型。用户自定义转换的取值以寻找从原类型到目标类型的特定的转换操作符为中心,具体可以划分为以下几个步骤:
寻找要进行用户自定义类型转换的类和结构体。此集合包括原类型和它的父类以及目标类型和它的父类(我们假设只有类和结构体可以声明用户自定义操作符,非类类型没有父类。)。
根据这个类型集合决定哪种用户自定义转换操作符适用。对于适用的转换操作符,它必须可以进行从原类型到操作符自变量类型的标准转换 (见§6.3 节),而且可以进行从操作符结果类型到目标类型的转换。
从这样使用的用户自定义操作符集合中找出最适用的操作符。总的来说,最适用的操作符的自变量类型与原类型最为接近,而且它的结果类型同目标类型最为接近。创建最使用用户自定义操作符的确切规则如下所示。
一旦特定的用户自定义转换操作符被确定,真正的用户自定义转换执行可以分为以下三个步骤:
1. 如果需要,进行一个标准转换把源数据类型转换为用户自定义转换操作符变量类型。
2. 启动用户自定义转换操作符进行转换
3. 如果需要,进行一个标准转换把用户自定义转换结果数据类型转换为目标数据类型。
用户自定义转换永远不会涉及到多余一个用户自定义转换操作符。换句话说,从类型S 到类型T 的用户自定义转换不会先进行一次从S 到X 的转换,之后再进行一次从X 到T 的转换。
下面是用户自定义显式或隐式转换取值的确切定义。定义使用如下术语:
如果存在一个由类型A 到类型B 的标准隐式转换,如果A 或者B 为接口类型,那么A 被B 包含,或者说B 包含A ) 。
在类型集合中的最大的包含类型是包含所有其余类型的类型。假如并不存在这样的一种类型,我们称这个类型集合没有最大包含类型。用更直观的话说,最大包含类型是集合中“最大”的类型-集合中所有其余类型都可以转换成这种类型。
在类型集合中的最大被包含类型是可以被其余所有类型包含的类型。如果集合中并不存在这样被所有其余类型包含的类型,那么我们称此集合没有最大被包含类型。用更直观的话说,最大包含类型是集合中“最小”的类型-这种类型可以转换成集合中所有其余类型。
4.3 用户自定义隐式转换
一个从类型S 到类型T 的用户自定义隐式转换依照下面所述方式进行:
寻找要进行用户自定义转换操作的类型集合,比如说我们称之为D。此集合包括S (S 为一个类和结构体)和S 的父类(如果S 是一个类),T(T 为一个类和结构体)和T 的父类(如果T 是一个类)。
寻找适用的用户自定义转换操作符,比如我们称之为U 。此集合包括若干在D 中声明的可以完成从一个包含S 的类型到一个被T 包含的类型之间转换的用户自定义隐式转换操作符。假如U 是空的,那么转换是未定义的,将会有错误发生。
找到U 中操作符最适用的源类型,比如说为Sx。
如果U 中任意操作符都是进行自S 的转换,那么Sx 就是S。
否则,Sx 是U 中操作符源类型的最大被包含类型。如果无法找到最大北被包含类型,那么转换将会发生错误。
找到U 中操作符最适用的目标类型,比如说为Tx。
如果U 中任意操作符都可以转换称类型T,那么Tx 就是T。
否则,Tx 是U 中操作符目标类型的最大包含类型。如果不存在这样的最大包含类型,那么转换将会发生错误。
假如U 中存在由类型Sx 到Tx 的用户自定义转换操作符,那么这个操作符就是最适用的转换操作符。如果不存在这样的操作符,或者存在不止一个这样的操作符,那么转换都会发生错误。
假如S 不是Sx,进行自S 到Sx 的标准隐式类型转换。
启动适用的操作符完成自Sx 到Tx 的转换。
假如Tx 不是T,进行自Tx 到T 的标准隐式类型转换。
4.4 用户自定义显式类型转换
一个从类型S 到类型T 的用户自定义显式转换依照下面所述方式进行:
寻找要进行用户自定义转换操作的类型集合,比如说我们称之为D。此集合包括S (S 为一个类和结构体)和S 的父类(如果S 是一个类),T(T 为一个类和结构体)和T 的父类(如果T 是一个类)。
寻找适用的用户自定义转换操作符,比如我们称之为U 。此集合包括若干在D 中声明的可以完成从一个包含S 的类型到一个被T 包含的类型之间转换的用户自定义隐式转换操作符。假如U 是空的,那么转换是未定义的,将会有错误发生。
找到U 中操作符最适用的源类型,比如说为Sx。
如果U 中任意操作符都是进行自S 的转换,那么Sx 就是S。
否则,假如U 中所有操作符源都是完成自包含S 的类型的转换,那么Sx 就是这些操作符源类型的最大被包含类型。如果无法找到最大北被包含类型,那么转换将会发生错误。
否则Sx 就是U 中操作符原类型的最大包含类型。如果不存在这样的最大包含类型,转换将发生错误。
找到U 中操作符最适用的目标类型,比如说为Tx。
如果U 中任意操作符都可以转换称类型T,那么Tx 就是T。
否则,如果如果U 中任意操作符都可以转换得到被T 包含的类型,那么Tx 就是这些操作符目标类型的最大包含类型。如果不存在这样的最大包含类型,转换将发生错误。
否则,Tx 是U 中操作符目标类型的最大包含类型。如果不存在这样的最大包含类型,那么转换将会发生错误。
假如U 中存在由类型Sx 到Tx 的用户自定义转换操作符,那么这个操作符就是最适用的转换操作符。如果不存在这样的操作符,或者存在不止一个这样的操作符,那么转换都会发生错误。
假如S 不是Sx,进行自S 到Sx 的标准显式类型转换。
启动适用的操作符完成自Sx 到Tx 的转换。
假如Tx 不是T,进行自Tx 到T 的标准显式类型转换。
在 C# 中,可以将转换声明为 implicit(需要时自动转换)或 explicit(需要调用转换)。所有转换都必须为 static,并且必须采用在其上定义转换的类型,或返回该类型。
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
五、二个自定义转换的例子。
下面来看二个自定义转换例子,第一个示例展示如何声明和使用转换,第二个示例演示结构之间的转换。
例1:
本示例中声明了一个 RomanNumeral 类型,并定义了与该类型之间的若干转换。
// conversion.cs
using System;
struct RomanNumeral
{
public RomanNumeral(int value)
{
this.value = value;
}
// Declare a conversion from an int to a RomanNumeral. Note the
// the use of the operator keyword. This is a conversion
// operator named RomanNumeral:
static public implicit operator RomanNumeral(int value)
{
// Note that because RomanNumeral is declared as a struct,
// calling new on the struct merely calls the constructor
// rather than allocating an object on the heap:
return new RomanNumeral(value);
}
// Declare an explicit conversion from a RomanNumeral to an int:
static public explicit operator int(RomanNumeral roman)
{
return roman.value;
}
// Declare an implicit conversion from a RomanNumeral to
// a string:
static public implicit operator string(RomanNumeral roman)
{
return("Conversion not yet implemented");
}
private int value;
}
class Test
{
static public void Main()
{
RomanNumeral numeral;
numeral = 10;
// Call the explicit conversion from numeral to int. Because it is
// an explicit conversion, a cast must be used:
Console.WriteLine((int)numeral);
// Call the implicit conversion to string. Because there is no
// cast, the implicit conversion to string is the only
// conversion that is considered:
Console.WriteLine(numeral);
// Call the explicit conversion from numeral to int and
// then the explicit conversion from int to short:
short s = (short)numeral;
Console.WriteLine(s);
}
}
输出10
例2:
本示例定义 RomanNumeral 和 BinaryNumeral 两个结构,并演示二者之间的转换。
// structconversion.cs
using System;
struct RomanNumeral
{
public RomanNumeral(int value)
{
this.value = value;
}
static public implicit operator RomanNumeral(int value)
{
return new RomanNumeral(value);
}
static public implicit operator RomanNumeral(BinaryNumeral binary)
{
return new RomanNumeral((int)binary);
}
static public explicit operator int(RomanNumeral roman)
{
return roman.value;
}
static public implicit operator string(RomanNumeral roman)
{
return("Conversion not yet implemented");
}
private int value;
}
struct BinaryNumeral
{
public BinaryNumeral(int value)
{
this.value = value;
}
static public implicit operator BinaryNumeral(int value)
{
return new BinaryNumeral(value);
}
static public implicit operator string(BinaryNumeral binary)
{
return("Conversion not yet implemented");
}
static public explicit operator int(BinaryNumeral binary)
{
return(binary.value);
}
private int value;
}
class Test
{
static public void Main()
{
RomanNumeral roman;
roman = 10;
BinaryNumeral binary;
// Perform a conversion from a RomanNumeral to a
// BinaryNumeral:
binary = (BinaryNumeral)(int)roman;
// Performs a conversion from a BinaryNumeral to a RomanNumeral.
// No cast is required:
roman = binary;
Console.WriteLine((int)binary);
Console.WriteLine(binary);
}
}
编译一下看看结果。看看能自己预想的是否一样?
上一章节,我们一起学习了C#的变量,这一章,我们来一起学习C#的转换。
C# 允许程序员在类或结构上声明转换,以便可以使类或结构与其他类或结构或者基本类型相互进行转换。转换的定义方法类似于运算符,并根据它们所转换到的类型命名。
C#的转换,又分为:1、隐式转换,2、显式转换,3、标准转换,4、用户自定义转换;下面,我们和大家一起学习:
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
一、隐式转换
下面的这几种类型的转换被称之为隐式转换
同一性转换
隐式数值转换
隐式枚举转换
隐式引用转换
包装转换
隐式常数表达式转换
用户自定义隐式转换
隐式转换可以在很多种情况下发生,包括函数子句调用,cast 计算表达式和赋值语句。
预定义的隐式转换总会成功而且不会引发异常,适当合理的用户自定义隐式转换也可以展示出这些特性。
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 到float 以及从long 到double 类型的转换可能会造成精度的损失,但并不会造成数量上的损失。除此之外的其他隐式数值转换不会损失任何信息。这里不存在转到char 类型的隐式数值转换,也就是说其他的整型数据不会被自动地转换为字符型数据。
1.3 隐式枚举转换
一个隐式枚举转换允许小数-整数实字(decimal-integer -literal)被转换成任意的枚举类型。
1.4 隐式引用转换
隐式reference 转换可以在下面类型之间发生:
从任意引用类型到对象。
从任意类类型S到任意类类型T, 只要S是由T 派生出来的。
从任意类类型S到任意接口类型T, 只要S实现T。
从任意接口类型S到任意接口类型T, 只要S是由T 派生出来的。
* 从一个带有元素类型S 的数组类型S到一个带有元素类型T 的数组类型T,只要下述各项均成立
S和T 只是元素类型不同。换句话说,S和T 有相同的维度。 S 和T 都是引用类型。
存在从S 到T 隐式引用转换。
从任意数组类型到System.Array。
从任意代表类型到System.Delegate。
从任意数组类型或代表类型到System.Icloneable。
从null 类型到任意引用类型。.
隐式引用转换指的是在引用类型间肯定可以成功的类型转换,它们是不需要实时检测的。
引用转换,不管是显式或是隐式的都不会改变被转换对象的引用一致性。换句话说,当引用转换发生时,它并未改变被转换对象的数值。
1.5 转换
包装转换允许任何数值类型被隐式地转换为类型对象,或者任何由这个数值类型实现的接口类型。
包装一个数值类型的数值包括分配一个对象实例并且将数值类型的数值复制到实例当中。
1.6 隐式常数表达式转换
隐式常数表达式转换允许下列类型的转换:
整型常数表达式可以被转换成为sbyte,byte,short,ushort,uint 或者ulong 类型,只有这个整型常数表达式的数值未超出目标类型的取值范围就行。
一个long 类型的常数表达式可以被转换成为ulong 类型,只有这个常数表达式的取值非负就行。
1.7 用户自定义隐式转换
一个用户自定义转换包括可选的标准隐式转换,紧随其后的是一个用户自定义的隐式转换运算符,之后是另一个可选的标准隐式转换。在§6.4.3 节中我们将详尽地描述用户自定义转换的取值规则。
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
二、显式转换
下列各种类型的转换属于显式转换:
所有隐式转换
显式数值转换
显式枚举转换
显式引用转换
显式引用转换
解包转换
用户自定义显式转换
显式转换可以在cast 表达式中出现。
显式转换并不总是能够成功,比如那些已知的损失信息的转换和那些足以使用显式说明的不同类型之间的转换。显式转换包括所有隐式转换,这就意味允许冗余的cast 表达式。
2.1 显式数值转换
显式数值转换指的是那些从一种数字类型到另外一种数字类型之间不能通过隐式类型转换实现的转换包括如下类型:
从sbyte 到byte,ushort,uint,ulong 或char 类型的转换。
从byte 到sbyte 和char 类型的转换。
从short 到sbyte,byteushort,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,shortushort,int,uint,long,ulong,char,float 或double 类型的转换。
由于形式类型转换包括所有隐式类型转换和显式数值转换,所以往往会出现使用cast 表达式完成从一种数字类型到另外一种数字类型的转换。
显式数值转换有可能造成自身信息损失或者异常。一个显式的数值转换是通过下述方式进行的:
对于从一种整型到另外一种整型的转换,具体过程取决于溢出检测的包含转换操的程序段: 在检测完成的程序段,如果源操作数未超出目标类型数据的取值范围,转换可以顺利完成;但是如果源操作数超出目标类型数据的取值范围,转换将生成一个溢出异常。在未完成检测的程序段,转换总是可以顺利完成,但是源操作数的最高位会被略去。
对于从float,double 或decimal 类型到整型的转换,源操作数是接近0 的整数,此值就是转换的结果。假如转换得到的整数超出目标类型允许的数值范围,转换将产生一个溢出异常。
对于从双精度到浮点数类型的转换,此双精度数值会逼近最为接近的浮点数数值。假如此双精度数太小无法使用浮点数表示,那么转换的结果将是0+或0-。假如此双精度数太大无法使用浮点数表示,那么转换结果将是-∞或+∞。假如此双精度数为NaN,那么转换的结果也是NaN 。
对于从浮点数或双精度到十进制类型的转换,源操作数会被转换成相应的十进制数,而当此十进制数达到或超过28 位长时,转换结果将是数值最接近的某一个十进制数。假如此源操作数太小无法用十进制数表示时,转换的结果将时0。当源操作数位NaN,∞或者超出十进制数的表示范围时,转换将产生一个InvalidCast 异常。
对于从十进制数到浮点数或双精度类型的转换,此十进制数会被转换为数值上最接近的浮点数或双精度数。虽然此转换会损失精度,但并不会造成异常。
2.2 显式枚举类型转换
显式枚举类型转换是指:
从sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double 或十进制到任意枚举类型的转换;
从任意枚举类型到sbyte,byte,short,ushort,int,uint,logn,ulong,char,floart,double 或十进制类型的转换;
从某一种枚举类型到另外一种枚举类型的转换 。
显式枚举类型转换是通过把源枚举类型看作此类型的原始类型,之后进行相应类型之间的显式或隐式类型转换。例如,如果有一个枚举类型E 且原始类型为int,那么从E 到byte 的转换将被作为一个显示的从int 到byte 的数字转换处理,而从byte 到的E 转换将被作为一个显示的从byte 到int的数字转换处理。
2.3 显式引用类型转换
显式引用类型转换指:
从对象(object)到任意引用类型之间的转换
从任意类类型S 到任意类类型T 之间的转换,其中S 是T 的基类。
从任意类类型 S 到任意类类型T 之间的转换,其中S 没有被封装,并且S 没有实现T。
从任意接口类型 S 到任意类类型T 之间的转换,其中T 没有被封装,并且T 没有实现S。
从任意接口类型S 到任意接口类型之间的转换,其中S 不是从T 派生的。
从任意具有元素SE 的数组类型S 到任意具有元素 TE 的数组类型T 之间的转换,只有满足下列条件才能进行:
S 和T 只在数字元素类型上存在不同。换句话说,S 和T 维数相同。
不论SE 或T E 都必须为引用类型。
从SE 或T E 之间的转换为显式引用转换。
从System.Array 到任意数组类型之间的转换。
从System.Delegate 到任意代表类型之间的转换。
从System.Icloneable 到任意数组类型或者代表类型之间的转换。
显式引用转换需要实时检测保证其正确进行的引用类型之间的转换。
对于一个null 类型或由源变量引用的实际的对象类型,必须是能够被隐式的引用转换来转换为目标类型的类型。假如显式引用转换失败,转换会生成一个InvalidCast 异常。
引用转换,无论是显式的或隐式的,并不改变被转换对象的引用一致性。换句话说,当一个引用转换改变数值的类型时,并未改变数值本身。
2.4 解包转换
解包转换允许从类型对象到任意数值类型,或者从任意接口类型到任意实现该接口类型的数值类型间的转换。解包操作包括首先检测对象实例为给定数值类型包装的数值,之后从那个实例中复制出数值。解包转换将在节中进一步说明。
2.5 用户自定义显式转换
用户自定义显式转换包括可选的标准显式转换,紧随其后的是一个用户自定义的显式转换运算符,之后是另一个可选的标准显式转换。在§6.4.4 节中我们将详尽地描述用户自定义转换的取值规则。
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
三、标准转换
标准转换指那些可以在用户自定义转换中出现的预定义的转换。
3.1 标准隐式转换
下列隐式转换属于标准隐式转换:
同一性类型转换
隐式数值类型转换
隐式引用转换
包装转换
隐式常数表达式转换
标准隐式转换是对立于用户自定义隐式转换而言的。
3.2 标准显式转换
标准显式转换包括所有标准隐式转换以及显式转换的子集,这是标准的隐式转换存在的原因。换句话说,假如存在一个从类型A 到类型B 的标准隐式转换,那么必然存在这两种类型相互之间的标准显式转换。
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
四、用户定义转换
C #允许可以被用户自定义转换调用的预定义显式和隐式转换。用户自定义转换是通过类和结构体中出现的转换运算符引入的。
4.1 允许的用户自定义转换
C #只允许某些用户自定义转换被声明。而且,它不允许重新定义一个已经存在的显式或者隐式转换。当所有下列的各项条件都满足时,类或结构体才被允许声明一个从源类型S 到目标类型T 的转换:
S 和T 是不同的类型
S 或者T 必须是包含转换操作符声明的类或结构类型。
S 或者T 是对象或一个接口类型。
T 不是S 的父类,S 也不是T 的父类。
4.2 用户自定义转换的取值
用户自定义转换将称作源类型的数值转换为另外一种称作目标类型的数据类型。用户自定义转换的取值以寻找从原类型到目标类型的特定的转换操作符为中心,具体可以划分为以下几个步骤:
寻找要进行用户自定义类型转换的类和结构体。此集合包括原类型和它的父类以及目标类型和它的父类(我们假设只有类和结构体可以声明用户自定义操作符,非类类型没有父类。)。
根据这个类型集合决定哪种用户自定义转换操作符适用。对于适用的转换操作符,它必须可以进行从原类型到操作符自变量类型的标准转换 (见§6.3 节),而且可以进行从操作符结果类型到目标类型的转换。
从这样使用的用户自定义操作符集合中找出最适用的操作符。总的来说,最适用的操作符的自变量类型与原类型最为接近,而且它的结果类型同目标类型最为接近。创建最使用用户自定义操作符的确切规则如下所示。
一旦特定的用户自定义转换操作符被确定,真正的用户自定义转换执行可以分为以下三个步骤:
1. 如果需要,进行一个标准转换把源数据类型转换为用户自定义转换操作符变量类型。
2. 启动用户自定义转换操作符进行转换
3. 如果需要,进行一个标准转换把用户自定义转换结果数据类型转换为目标数据类型。
用户自定义转换永远不会涉及到多余一个用户自定义转换操作符。换句话说,从类型S 到类型T 的用户自定义转换不会先进行一次从S 到X 的转换,之后再进行一次从X 到T 的转换。
下面是用户自定义显式或隐式转换取值的确切定义。定义使用如下术语:
如果存在一个由类型A 到类型B 的标准隐式转换,如果A 或者B 为接口类型,那么A 被B 包含,或者说B 包含A ) 。
在类型集合中的最大的包含类型是包含所有其余类型的类型。假如并不存在这样的一种类型,我们称这个类型集合没有最大包含类型。用更直观的话说,最大包含类型是集合中“最大”的类型-集合中所有其余类型都可以转换成这种类型。
在类型集合中的最大被包含类型是可以被其余所有类型包含的类型。如果集合中并不存在这样被所有其余类型包含的类型,那么我们称此集合没有最大被包含类型。用更直观的话说,最大包含类型是集合中“最小”的类型-这种类型可以转换成集合中所有其余类型。
4.3 用户自定义隐式转换
一个从类型S 到类型T 的用户自定义隐式转换依照下面所述方式进行:
寻找要进行用户自定义转换操作的类型集合,比如说我们称之为D。此集合包括S (S 为一个类和结构体)和S 的父类(如果S 是一个类),T(T 为一个类和结构体)和T 的父类(如果T 是一个类)。
寻找适用的用户自定义转换操作符,比如我们称之为U 。此集合包括若干在D 中声明的可以完成从一个包含S 的类型到一个被T 包含的类型之间转换的用户自定义隐式转换操作符。假如U 是空的,那么转换是未定义的,将会有错误发生。
找到U 中操作符最适用的源类型,比如说为Sx。
如果U 中任意操作符都是进行自S 的转换,那么Sx 就是S。
否则,Sx 是U 中操作符源类型的最大被包含类型。如果无法找到最大北被包含类型,那么转换将会发生错误。
找到U 中操作符最适用的目标类型,比如说为Tx。
如果U 中任意操作符都可以转换称类型T,那么Tx 就是T。
否则,Tx 是U 中操作符目标类型的最大包含类型。如果不存在这样的最大包含类型,那么转换将会发生错误。
假如U 中存在由类型Sx 到Tx 的用户自定义转换操作符,那么这个操作符就是最适用的转换操作符。如果不存在这样的操作符,或者存在不止一个这样的操作符,那么转换都会发生错误。
假如S 不是Sx,进行自S 到Sx 的标准隐式类型转换。
启动适用的操作符完成自Sx 到Tx 的转换。
假如Tx 不是T,进行自Tx 到T 的标准隐式类型转换。
4.4 用户自定义显式类型转换
一个从类型S 到类型T 的用户自定义显式转换依照下面所述方式进行:
寻找要进行用户自定义转换操作的类型集合,比如说我们称之为D。此集合包括S (S 为一个类和结构体)和S 的父类(如果S 是一个类),T(T 为一个类和结构体)和T 的父类(如果T 是一个类)。
寻找适用的用户自定义转换操作符,比如我们称之为U 。此集合包括若干在D 中声明的可以完成从一个包含S 的类型到一个被T 包含的类型之间转换的用户自定义隐式转换操作符。假如U 是空的,那么转换是未定义的,将会有错误发生。
找到U 中操作符最适用的源类型,比如说为Sx。
如果U 中任意操作符都是进行自S 的转换,那么Sx 就是S。
否则,假如U 中所有操作符源都是完成自包含S 的类型的转换,那么Sx 就是这些操作符源类型的最大被包含类型。如果无法找到最大北被包含类型,那么转换将会发生错误。
否则Sx 就是U 中操作符原类型的最大包含类型。如果不存在这样的最大包含类型,转换将发生错误。
找到U 中操作符最适用的目标类型,比如说为Tx。
如果U 中任意操作符都可以转换称类型T,那么Tx 就是T。
否则,如果如果U 中任意操作符都可以转换得到被T 包含的类型,那么Tx 就是这些操作符目标类型的最大包含类型。如果不存在这样的最大包含类型,转换将发生错误。
否则,Tx 是U 中操作符目标类型的最大包含类型。如果不存在这样的最大包含类型,那么转换将会发生错误。
假如U 中存在由类型Sx 到Tx 的用户自定义转换操作符,那么这个操作符就是最适用的转换操作符。如果不存在这样的操作符,或者存在不止一个这样的操作符,那么转换都会发生错误。
假如S 不是Sx,进行自S 到Sx 的标准显式类型转换。
启动适用的操作符完成自Sx 到Tx 的转换。
假如Tx 不是T,进行自Tx 到T 的标准显式类型转换。
在 C# 中,可以将转换声明为 implicit(需要时自动转换)或 explicit(需要调用转换)。所有转换都必须为 static,并且必须采用在其上定义转换的类型,或返回该类型。
-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------
五、二个自定义转换的例子。
下面来看二个自定义转换例子,第一个示例展示如何声明和使用转换,第二个示例演示结构之间的转换。
例1:
本示例中声明了一个 RomanNumeral 类型,并定义了与该类型之间的若干转换。
// conversion.cs
using System;
struct RomanNumeral
{
public RomanNumeral(int value)
{
this.value = value;
}
// Declare a conversion from an int to a RomanNumeral. Note the
// the use of the operator keyword. This is a conversion
// operator named RomanNumeral:
static public implicit operator RomanNumeral(int value)
{
// Note that because RomanNumeral is declared as a struct,
// calling new on the struct merely calls the constructor
// rather than allocating an object on the heap:
return new RomanNumeral(value);
}
// Declare an explicit conversion from a RomanNumeral to an int:
static public explicit operator int(RomanNumeral roman)
{
return roman.value;
}
// Declare an implicit conversion from a RomanNumeral to
// a string:
static public implicit operator string(RomanNumeral roman)
{
return("Conversion not yet implemented");
}
private int value;
}
class Test
{
static public void Main()
{
RomanNumeral numeral;
numeral = 10;
// Call the explicit conversion from numeral to int. Because it is
// an explicit conversion, a cast must be used:
Console.WriteLine((int)numeral);
// Call the implicit conversion to string. Because there is no
// cast, the implicit conversion to string is the only
// conversion that is considered:
Console.WriteLine(numeral);
// Call the explicit conversion from numeral to int and
// then the explicit conversion from int to short:
short s = (short)numeral;
Console.WriteLine(s);
}
}
输出10
例2:
本示例定义 RomanNumeral 和 BinaryNumeral 两个结构,并演示二者之间的转换。
// structconversion.cs
using System;
struct RomanNumeral
{
public RomanNumeral(int value)
{
this.value = value;
}
static public implicit operator RomanNumeral(int value)
{
return new RomanNumeral(value);
}
static public implicit operator RomanNumeral(BinaryNumeral binary)
{
return new RomanNumeral((int)binary);
}
static public explicit operator int(RomanNumeral roman)
{
return roman.value;
}
static public implicit operator string(RomanNumeral roman)
{
return("Conversion not yet implemented");
}
private int value;
}
struct BinaryNumeral
{
public BinaryNumeral(int value)
{
this.value = value;
}
static public implicit operator BinaryNumeral(int value)
{
return new BinaryNumeral(value);
}
static public implicit operator string(BinaryNumeral binary)
{
return("Conversion not yet implemented");
}
static public explicit operator int(BinaryNumeral binary)
{
return(binary.value);
}
private int value;
}
class Test
{
static public void Main()
{
RomanNumeral roman;
roman = 10;
BinaryNumeral binary;
// Perform a conversion from a RomanNumeral to a
// BinaryNumeral:
binary = (BinaryNumeral)(int)roman;
// Performs a conversion from a BinaryNumeral to a RomanNumeral.
// No cast is required:
roman = binary;
Console.WriteLine((int)binary);
Console.WriteLine(binary);
}
}
编译一下看看结果。看看能自己预想的是否一样?