第5章 类型转换
C#是静态类型的语言,变量一旦声明就无法重新声明或者存储其他类型的数据,除非进行类型转换。本章的主要任务就是学习类型转换的知识。类型转换有显式的,也有隐式的。所谓显式,就是我们必须明确地告知编译器,我们要把变量从源类型转换成什么类型;而隐式的则不需要,编译器会自动帮我们进行转换。实际上,截至目前,我们已经学习过一部分类型转换的知识了,还记得第1章讲的装箱和拆箱吗?我们将在本章中进一步学习装箱和拆箱的知识。
准备好了吗?我们开始吧!
5.1 隐式类型转换
什么是隐式转换呢?如果编译器认为从类型1(下称T1)到类型2(下称T2)的转换不会产生不良后果,那么T1到T2的转换就是由编译器自动完成的,这就是隐式转换。我们举个例子,如代码清单5-1所示。
代码清单5-1 隐式类型转换
1 namespace ProgrammingCSharp4
2{
3 class TypeConvert
4{
5 private void DoSomething()
6{
7 int intValue=10;
8 long longValue=intValue;
9}
10}
11}
第8行执行的是int型到long型的转换,long型对应的是System.Int64,int型对应的是System.Int32,显然long型的取值范围要比int型大,因此这种转换是安全的,编译器允许了此次转换。为了了解类型转换的实质,我们可以通过查看上述代码编译后生成的CIL代码[1],如代码清单5-2所示。
代码清单5-2 CIL代码
1.method private hidebysig instance void DoSomething()cil managed
2{
3//Code size 8(0x8)
4.maxstack 1
5.locals init([0]int32 intValue,
6[1]int64 longValue)
7 IL_0000:nop
8 IL_0001:ldc.i4.s 10
9 IL_0003:stloc.0
10 IL_0004:ldloc.0
11 IL_0005:conv.i8
12 IL_0006:stloc.1
13 IL_0007:ret
14}//end of method TypeConvert:DoSomething
为了突出重点,我们先忽略其他不相关内容,只关注与类型转换相关的CIL指令。
第8行:类型为i4(即int32)的数据10,入栈;
第9行:出栈,赋予变量[0],即intValue;
第10行:变量0数据入栈;
第11行:将栈顶中的数据转换为i8类型(即int64,也就是long类型);
第12行:出栈,赋予变量[1],即longValue;
……
其中,最重要的是第11行,编译器生成了类型转换的CIL指令:conv,该指令的语法为:
conv.<to type>
<to type>就是要转换到的目标类型。
可见,查看CIL代码有助于我们了解编译器所做的实际操作,有助于我们更加深刻地理解C#这门语言,以及.NET CLR的一些工作机制。在本书的其他章节,我们还会通过CIL代码来进行学习。由于CIL的知识超出了本书的范围,需要进一步了解CIL的读者,可以自行查阅其他资料。
大家现在应该对隐式类型转换有了初步的了解,接下来将进一步学习数值类型的隐式转换,以及引用类型中的隐式转换。
5.1.1 数值类型
C#语言支持的数值类型的隐式转换如下所示:
❑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。
上述的隐式转换是安全的,不会造成任何精度或者数量级的损失。需要说明的是,C#不支持任何其他类型到char类型的隐式转换。
有两种特殊的隐式转换需要说明,之所以说它们特殊,是因为它们会带来精度损失,但没有数量级损失,它们是:
❑从int、uint、long或者ulong到float的转换;
❑从long或者ulong到double的转换。
我们还是以一段代码为例,演示从int到float的类型转换,以此演示精度损失的情况,如代码清单5-3所示。
代码清单5-3 精度损失示例
1 using System;
2
3 namespace ProgrammingCSharp4
4{
5 class TypeConvert
6{
7 static void Main(string[]args)
8{
9 TypeConvert typeConvert=new TypeConvert();
10 typeConvert.DoSomething();
11}
12
13 public void DoSomething()
14{
15 int max=int.MaxValue;
16 float floatValue=max;
17 Console.WriteLine(max);
18 Console.WriteLine(floatValue);
19}
20}
21}
上述代码打印了int类型支持的最大有效值(MaxValue),然后将它赋予了一个float类型的变量floatValue,编译器执行了隐式转换。运行结果是:
2147483647
2.147484E+09[2]
可以看出,转换后的数值比原值的有效位减少了,因为原值是2 147 483 647(有效位:10),转换后的值是2 147 484 000(有效位:7),很显然,类型转换造成了精度损失,但数量级并没有损失。至于另外一种情况——从long或ulong到double的转换,请大家自行验证。
我们在学习程序设计时一定要重视实验(注意不是“试验”,而是实地验证),将书本或者课题上讲的内容、知识点进行实际验证。这可以加深我们对知识的理解,同时也能积累解决问题的方式和方法。
下一节我们将讲述引用类型的隐式转换。
[1]要查看CIL代码,可以依次点击:开始→所有程序→Visual Studio 2010→Microsoft Windows SDK Tools→IL反汇编程序,然后在“IL反汇编程序”中打开编译后生成的exe或dll文件,这取决于项目的类型,该文件一般位于:项目目录\bin\Debug,或者“项目目录\bin\Release”。
[2]这里2.147484E+09表示科学计数法,相当于2.147 484×10+09,也就是2 147 484 000。