• 第5章 类型转换
    • 5.1 隐式类型转换">5.1 隐式类型转换
      • 5.1.1 数值类型">5.1.1 数值类型

    第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。