jhh0111

常用链接

统计

最新评论

浅谈C#基本数字数据类型。

众所周知,像“int a = 10; short b = a“这样的语句是无法编译通过的,原因是cannot implicitly convert type 'int' to 'short'。而我写上“short = 10”这样的语句是没有问题的,即没有错误也没有警告,这是为什么呢,难道编译器自动帮我加上强制类型转换?为了揭开这些方面的谜题,我做了些测试,因此有了本文。

C#基本数字数据类型一共有11种,其中8种整数类型(byte, sbyte, short, ushort, int, uint, long, ulong),3种可带小数类型(double, float, decimal).首先,我对8种整数类型做了如下测试:

    class Program
    {
        public static byte _byte;
        public static sbyte _sbyte;
        public static short _short;
        public static ushort _ushort;
        public static int _int;
        public static uint _uint;
        public static long _long;
        public static ulong _ulong;

        public static byte _byte2;
        public static short _short2;
        public static uint _uint2;
        public static long _long2;
       
        static void Main(string[] args)
        {
            _byte = 20;
            _sbyte = 20;
            _short = 20;
            _ushort = 20;
            _int = 20;
            _uint = 20;
            _long = 20;
            _ulong = 20;

            _byte2 = (byte)20;
            _short2 = (short)20;
            _uint2 = 20U;
            _long2 = 20L;
        }
    }

生成的IL代码如下:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       89 (0x59)
  .maxstack  1
  IL_0000:  nop
  IL_0001:  ldc.i4.s   20
  IL_0003:  stsfld     uint8 ConsoleApplication1.Program::_byte
  IL_0008:  ldc.i4.s   20
  IL_000a:  stsfld     int8 ConsoleApplication1.Program::_sbyte
  IL_000f:  ldc.i4.s   20
  IL_0011:  stsfld     int16 ConsoleApplication1.Program::_short
  IL_0016:  ldc.i4.s   20
  IL_0018:  stsfld     uint16 ConsoleApplication1.Program::_ushort
  IL_001d:  ldc.i4.s   20
  IL_001f:  stsfld     int32 ConsoleApplication1.Program::_int
  IL_0024:  ldc.i4.s   20
  IL_0026:  stsfld     uint32 ConsoleApplication1.Program::_uint
  IL_002b:  ldc.i4.s   20
  IL_002d:  conv.i8
  IL_002e:  stsfld     int64 ConsoleApplication1.Program::_long
  IL_0033:  ldc.i4.s   20
  IL_0035:  conv.i8
  IL_0036:  stsfld     uint64 ConsoleApplication1.Program::_ulong
  IL_003b:  ldc.i4.s   20
  IL_003d:  stsfld     uint8 ConsoleApplication1.Program::_byte2
  IL_0042:  ldc.i4.s   20
  IL_0044:  stsfld     int16 ConsoleApplication1.Program::_short2
  IL_0049:  ldc.i4.s   20
  IL_004b:  stsfld     uint32 ConsoleApplication1.Program::_uint2
  IL_0050:  ldc.i4.s   20
  IL_0052:  conv.i8
  IL_0053:  stsfld     int64 ConsoleApplication1.Program::_long2
  IL_0058:  ret
} // end of method Program::Main


我们发现,_byte = 20 与 _byte = (byte)20 生成的代码一模一样,_long = 20 与 _long = 20L生成的代码也一模一样。因此,结合一些其他测试(这里省略) ,我们得出以下结论:
1、_byte = 20 与 _byte = (byte)20 的效益一样。
2、_long = 20 与 _long = 20L 的效益一样,20L中的“L”仅仅在编译中起作用。
3、编译器会自动检测直接赋的值是否超过该类型所能表示的最大范围. ( _byte = 256 会导致编译出错)


接下来我们对double,float,decimal进行测试:(注释的行表示会导致编译无法通过)

    class Program
    {
        public static float _float;
        public static double _double;
        public static decimal _decimal;
 
        static void Main(string[] args)
        {
            _float = 5;
            //_float = 5.0;
            _float = 5F;
            //_float = 5M;

            _double = 5;
            _double = 5.0;
            _double = 5F;
            //_double = 5M;

            _decimal = 5;
            //_decimal = 5.0;
            _decimal = 5.0M;
            //_decimal = 5.0F;
        }
    }

IL代码如下:
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       91 (0x5b)
  .maxstack  6
  IL_0000:  nop
  IL_0001:  ldc.r4     5.
  IL_0006:  stsfld     float32 ConsoleApplication1.Program::_float
  IL_000b:  ldc.r4     5.
  IL_0010:  stsfld     float32 ConsoleApplication1.Program::_float
  IL_0015:  ldc.r8     5.
  IL_001e:  stsfld     float64 ConsoleApplication1.Program::_double
  IL_0023:  ldc.r8     5.
  IL_002c:  stsfld     float64 ConsoleApplication1.Program::_double
  IL_0031:  ldc.r8     5.
  IL_003a:  stsfld     float64 ConsoleApplication1.Program::_double
  IL_003f:  ldc.i4.5
  IL_0040:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32)
  IL_0045:  stsfld     valuetype [mscorlib]System.Decimal ConsoleApplication1.Program::_decimal
  IL_004a:  ldc.i4.s   50
  IL_004c:  ldc.i4.0
  IL_004d:  ldc.i4.0
  IL_004e:  ldc.i4.0
  IL_004f:  ldc.i4.1
  IL_0050:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32,
                                                                     int32,
                                                                     int32,
                                                                     bool,
                                                                     uint8)
  IL_0055:  stsfld     valuetype [mscorlib]System.Decimal ConsoleApplication1.Program::_decimal
  IL_005a:  ret
} // end of method Program::Main

结论:
1、跟整数类型不同,带小数类型在直接赋数值时必须指定相应数值类型 或 可由该默认数值类型隐式转化为该变量类型。
2、_float = 5 与 _float = 5F 的效率一样。"F" 同样只在编译中起作用。
3、decimal类型的赋值跟其它类型有些不同,查decimal的构造方法发现,它有9个公有构造方法。

以上就是我对数值类型的简单分析,如有不足或错误,欢迎大家指出。

附:
    L 表示 long
    D 表示 double
    F 表示 float
    M 表示 decimal
    U 表示 uint
    UL 表示 ulong

posted on 2008-04-22 14:56 中华小鹰 阅读(387) 评论(3)  编辑 收藏

评论

#1楼  2008-04-22 18:44 没有昵称      

short b = a编译不通过的原因,是a是整型,整型的取值范围大于short,将a的值复制给b时会造成截断。所以编译器会产生一个错误,要求你使用强制类型转换。实际上也就是提示你:你应该在代码中保证a的值在short类型的取值范围内。否则试想,如果a = 32768,你执行b = a,你觉得b的值会是多少?

short b = 10能够编译通过的原因,是编译器知道10是一个有效的short值,在-32,768 到 32,767之间。如果你写了一句short b = 32768,仍然会出编译错误。

至于你怀疑为什么short b = a不行而short b = 10却没问题,可以看出你有个基本概念不清楚:虽然a = 10,但这两句话是绝对不一样的。第二句话产生的cup指令是“将常数10 赋值到b所在的16位内存地址(因为是short)上去”,而第一句产生的cpu指令是“1、从a所在的32位(因为a是int类型)内存中把数据读出来;2、把这个数值复制给b所在的16位内存地址上去”。

因此可见你对汇编的知识已经忘的差不多了吧。在编程的时候,要用计算机的思维方式去思考,而不是用人的思维方式去思考。

既然你已经在观察IL代码了,那何不观察一下short b = a和short b = 10所产生的IL代码有何不同?   回复  引用  查看    

#2楼  2008-04-23 09:20 李战      

和楼主一起学习。   回复  引用  查看    

#3楼 [楼主] 2008-04-23 22:29 中华小鹰      

@没有昵称

可能我没表达明白我的意思,我当然知道short,int在内存的表示,我要探究的就是编译器是怎么处理我们的代码的。

比如 float _float = 5.0;
如果有“足够聪明”的编译器的话,这行语句就不会无法编译通过了,但我们发现C#编译器无法编译通过。

当然,我也不是说C#编译器不足够聪明,这就是我前面这段加引号的原因,因为要做到类型安全,尽可能的在编译阶段发现潜在的问题,是有好处的。

因此,我考虑的是,像 short _short = 10 这样的语句,编译器是怎么处理的,是不做溢出检查的直接生成IL代码(如此则代码存在溢出隐患),还是生成在IL代码中做溢出检查的IL代码(如此则有性能损失)。当然,最后我搞明白的是,这两者都不是,编译器在编译过程中做溢出检查,最后的IL代码是不做溢出检查的,这样,就即没有溢出隐患,也不会有性能损失(这个结论才是我最后需要的,而不是我不懂汇编,没有从机器的角度思考问题,实际上,我用80*86汇编写过完整的代码,怎么可能会不知道汇编)。

在没有做上面测试前,我问你,short _short1 = 10; 与 short _short2 = (short)10; 这两句,哪句性能更好,哪句安全性更高(或者说隐患更少),你会怎么回答。

实际结论是,都一样。   回复  引用  查看    


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-04-22 15:01 编辑过
 
另存  打印