转换:使一种类型的表达式可以被视为另一种类型。转换有两种形式:隐式的、显式的;
隐式转换:这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失,所以预定义的隐式转换从来不会引发异常;
显式转换:显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。
6.1 隐式转换
属于隐式转换的有:标识转换、隐式数值转换、隐式枚举转换、隐式引用转换、装箱转换、隐式常数表达式转换、用户自定义的隐式转换;
6.1.1 标识转换
标识转换是在同一类型内进行转换。这种转换的存在仅仅是为了使已具有所需类型的实体可被认为是可转换的。
6.1.2 隐式数值转换
从int,uint,long或ulong到float,以及从long或ulong到double的转换可能导致精度损失,但不会影响它的数量级,其他的不会损失任何信息。
6.1.3 隐式枚举转换
隐式枚举转换允许将十进制整数0转换为任何枚举类型。
6.1.4 隐式引用转换
引用转换无论是隐式的还是显式的,都不会更改所转化的对象的引用标识,换言之,虽然引用转化可能改变该引用的类型,但绝不会更改所引用对象的类型或值。
6.1.5 装箱转换
将值类型隐式转换为引用类型,以前的文章写过,就不多说了。
6.1.6 隐式常数表达式转换
6.1.7 用户定义的隐式转换
用户定义的隐式转换由三个部分组成:1.先是一个标准的隐式转换(可选);
2.然后是执行用户定义的隐式转换运算符;
3.最后是另一个标准的隐式转换(可选)。
6.2显式转换
属于显式转换的由:所有隐式转换、显式数值转换、显式枚举转换、显式引用转换、显式接口转换、拆箱、用户自定义的显式转换;显式转换包括所有隐式转换,这意味着允许使用冗余的强制转换表达式;显式转换中除了隐式转换,其他转换不能保证总是成功,知道有可能丢失信息,变化前后的类型显著不同,从而值得使用显式表示法。
6.2.1 显式数值转换
显式数值转换是指从一个数值类型转换到另一个数值类型,此转换不能用已知的隐式数值转换实现。
由于显式转换包括所有隐式和显式数值转换,因此总是可以使用强制转换表达式,显式转换可能丢失信息和导致引发异常,处理流程是:
对于一个整型到另一个整型的转换,处理取决于该转换发生时的溢出检查上下文:
在checked上下文中,如果源操作数的值在目标类型的范围内,则成功;若超出范围,会引发System.OverflowException;
在unchecked上下文中,转换总是成功,因为若源类型大于目标类型,则截断源值,即截去源值中容不下的最高有效位后视为目标类型的值;若源类型小于目标类型,则源值按符号扩展(源类型是有符号的)或按零扩展(源类型是没符号的),以使它的大小与目标类型相同;相同时直接视为值。
对于从decimal到整型的转换,源值向零舍入到最接近的整数值后作为转换结果;若转化的结果不再范围,会引发System.OverflowException;
对于从float/double到整型的转换,处理取决于发生该转化时的溢出检查上下文:
在checked上下文中,若操作数是NaN或无穷大,会引发System.OverflowException;否则源操作数会向零舍入到最接近的整数值后作为转换结果;若转化结果不再范围,会引发System.OverflowException;
在unchecked上下文中,若果操作数是NaN或infinite,则转化结果是目标类型的一个未经指定的值;否则源操作数会向零舍入到最接近的整数值后作为转换结果;若转化结果不再范围转化结果是目标类型的一个未经指定的值。
对于double转到float,double值舍入到最接近的float值。如果double值过小,无法表示为float值,结果变成正零或负零;若过大,无法表示为float值,结果变成正无穷大或负无穷大;若果double值为NaN,则结果仍是NaN。
对于从float或double到decimal的转换,源值转化后,在需要时将在第28位小数位上舍入到最接近的数字。若果源值过小,无法表示为decimal,结果变成零;若源值为NaN、无穷大或无法表示为decimal值,将引发System.OverflowException;
对于decimal转到float或double,值会舍入到最接近double或float的值,虽然会损失精度,但不会引发异常。
6.2.2 显式枚举转换
两种类型间的显式枚举转换是通过任何参与的枚举类型都按该枚举类型的基础类型处理的,然后在结果类型之间执行隐式或显式数值转换。
6.2.3显式引用转换
显式引用转换是那些需要运行时检查以确保它们正确的引用类型之间的转换;为了是显式引用转换在允许时成功,源操作数的值必须为null或源操作数所引用的对象的实际类型必须是要一个可通过隐式引用转换转换成目标类型的类型。如果转换失败会引发System.InvalidCastException。
无论隐式还是显式引用转换,都不会更改所转化的对象的引用标识,换言之,虽然引用转化可能改变该引用的类型,但绝不会更改所引用对象的类型或值。
6.2.4 拆箱
将引用类型显式转化成值类型,以前和装箱一起说过。
6.2.5用户定义的显式转换
用户定义的显式转换由三个部分组成:1.先是一个标准的显式转换(可选);
2.然后是执行用户定义的隐式或显式转换运算符;
3.最后是另一个标准的显式转换(可选)。
6.3标准转换
标准转换是那些预先定义的转换,它们可以作为用户定义转换的组成部分出现。
标准隐式转换:标识转换、隐式数值转换、隐式引用转换、装箱转换、隐式常数表达式转换;
标准显式转换:包括所有的标准隐式转换,以及一个显式转换的子集,该子集由那些与已知的标准隐式转换反向的转换组成;意思就是如果存在一个从A类型到B类型的标准隐式转换,则一定存在与其对应的两个标准显示准换(一个A转B,一个B转A)。
6.4用户定义的缓缓
C#只允许声明某些用户定义的转换。具体来说,就是不可能重新定义已存在的隐式或显式转换。仅当以下条件为真时,才允许类或结构声明从源类型S到目标类型T的转换。
用户定义的转换的计算集中在查找符合特定的源类型和目标类型的最精确的用户定义转换运算符,分为几个步骤:
查找考虑从中使用用户定义的转换运算符的类和结构集。此集由源类型及其基类、目标类型及其基类组成;
由该类型集确定合适的用户定义转换运算符。满足下述条件就是适用:必须通过执行标准转换来使源类型转换为该运算符的操作数所要求的类型,并且必须通过标准转换来使运算符的结果转化为目标类型;
由适用的用户定义运算符集,明确地表明哪一个运算符最精确。
确定了最精确的用户定义转换运算符后,用户定义转换的实际执行包括三个步骤:
首先,如果需要,执行一个标准转换,将源类型转化为用户定义转换运算符的操作数所要求的类型;
然后,调用用户定义转换运算符以执行转换;
最后,如果需要,再执行一个标准转换,将结果类型转换为目标类型。
最后注意:用户定义转换的计算从不涉及一个以上的用户定义转换运算符。即从S类型到T类型的转换,绝不会首先执行从S到X的用户定义转换,然后执行X到T的用户转换。