none
关于装箱和拆箱的问题 RRS feed

  • 问题

  • http://blog.csdn.net/bjd14/archive/2009/04/30/4139882.aspx
    看了这篇文章,能否解释一下下面的情况:

    但是要注意一种情况,如下示例:

    string a = "this is string a";
                 string b = a;
                 a = "this is not string a now!";
                 Console.WriteLine("Now a is: {0}", a);
                 Console.WriteLine("Now b is: {0}", b);

    或:

    Object a = 10;
                 Object b = a;
                 a = 20;
                 Console.WriteLine(a.ToString());
                 Console.WriteLine(b.ToString());

    这里的b不是指向a的地址,而是重新开了另一个内存空间,所以这里显示的结果a和b是绝对不相同的!

    这个地方为什么会产生这样的结果还不是很明白....

    另外问一下,有了泛型,装箱和拆箱的用处还大么?

    2009年12月2日 8:48

答案

  • 你好!
         我详细解释一下两种情况,希望对你有帮助:
         第一种情况:
         string 为引用类型,所以这段代码不存在装箱操作,在.NET下对string类型会进行特殊的处理,使他看起来更像值类型,怎样特殊处理的呢?
         很简单,只要string变量的值改变,就在托管堆中新创建一个实例,并让原引用指向这个新创建的实例。
         string a = "this is string a";
                 string b = a;     //这时b和a都指向内容是”this is string a“的那个实例
                 a = "this is not string a now!";     //这时改变了a的值,会新创建一个实例,并且让a指向这个新实例,这时b还是指向原实例的
                 Console.WriteLine("Now a is: {0}", a);     //这里输出
    this is not string a now!
                 Console.WriteLine("Now b is: {0}", b);     //这里输出 this is string a

        第二中情况:
       
    Object a = 10;  //这里发生了装箱,a指向内容是10的那个实例
                 Object b = a;       //这里让b也指向了
    内容是10的那个实例
                 a = 20;      //这里发生了装箱,a指向了内容是20的新创建的实例,装箱过程本身会创建新实例的
                 Console.WriteLine(a.ToString());   //输出20
                 Console.WriteLine(b.ToString());     //输出10

    周雪峰
    2009年12月2日 10:27
    版主
  • Hi,
    参与讨论一下。
    1.引用类型的赋值问题:
    String 和引用类型应该分开讨论。虽然String是引用类型,但是因为.NET对其提供了不同的特性和内部处理机制,所以简单比较不好。反而容易造成误解。

    这个测试语句写的也不好。特别是关于String的。不应该简单来做比较。

    2.泛型的问题:泛型确实能避免装箱和拆箱带来的性能问题。但是,不是.NET 类库的所哟类型都支持泛型,有时候还是需要使用装箱和拆箱的。
    Frank Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    欢迎访问老徐的中文技术博客:Welcome to My Chinese Technical Blog
    欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
    欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum
    2009年12月2日 10:32
    版主
  • 补充一下,实际上我们可以看一下第二中情况生成的IL代码,你直接看我的注释就可以了:
      .entrypoint
      // 代码大小       44 (0x2c)
      .maxstack  1
      .locals init ([0] object a,
               [1] object b)
      IL_0000:  nop
      IL_0001:  ldc.i4.s   10
      IL_0003:  box        [mscorlib]System.Int32      //对10进行装箱
      IL_0008:  stloc.0    //a指向内容是10的实例
      IL_0009:  ldloc.0
      IL_000a:  stloc.1     //b指向内容是10的实例
      IL_000b:  ldc.i4.s   20     
      IL_000d:  box        [mscorlib]System.Int32     //对20进行装箱
      IL_0012:  stloc.0    //a指向内容是20的实例
      IL_0013:  ldloc.0
      IL_0014:  callvirt   instance string [mscorlib]System.Object::ToString()
      IL_0019:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_001e:  nop
      IL_001f:  ldloc.1
      IL_0020:  callvirt   instance string [mscorlib]System.Object::ToString()
      IL_0025:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_002a:  nop
      IL_002b:  ret
    周雪峰
    2009年12月2日 10:55
    版主

全部回复

  • 你好!
         我详细解释一下两种情况,希望对你有帮助:
         第一种情况:
         string 为引用类型,所以这段代码不存在装箱操作,在.NET下对string类型会进行特殊的处理,使他看起来更像值类型,怎样特殊处理的呢?
         很简单,只要string变量的值改变,就在托管堆中新创建一个实例,并让原引用指向这个新创建的实例。
         string a = "this is string a";
                 string b = a;     //这时b和a都指向内容是”this is string a“的那个实例
                 a = "this is not string a now!";     //这时改变了a的值,会新创建一个实例,并且让a指向这个新实例,这时b还是指向原实例的
                 Console.WriteLine("Now a is: {0}", a);     //这里输出
    this is not string a now!
                 Console.WriteLine("Now b is: {0}", b);     //这里输出 this is string a

        第二中情况:
       
    Object a = 10;  //这里发生了装箱,a指向内容是10的那个实例
                 Object b = a;       //这里让b也指向了
    内容是10的那个实例
                 a = 20;      //这里发生了装箱,a指向了内容是20的新创建的实例,装箱过程本身会创建新实例的
                 Console.WriteLine(a.ToString());   //输出20
                 Console.WriteLine(b.ToString());     //输出10

    周雪峰
    2009年12月2日 10:27
    版主
  • Hi,
    参与讨论一下。
    1.引用类型的赋值问题:
    String 和引用类型应该分开讨论。虽然String是引用类型,但是因为.NET对其提供了不同的特性和内部处理机制,所以简单比较不好。反而容易造成误解。

    这个测试语句写的也不好。特别是关于String的。不应该简单来做比较。

    2.泛型的问题:泛型确实能避免装箱和拆箱带来的性能问题。但是,不是.NET 类库的所哟类型都支持泛型,有时候还是需要使用装箱和拆箱的。
    Frank Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    欢迎访问老徐的中文技术博客:Welcome to My Chinese Technical Blog
    欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
    欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum
    2009年12月2日 10:32
    版主
  • 补充一下,实际上我们可以看一下第二中情况生成的IL代码,你直接看我的注释就可以了:
      .entrypoint
      // 代码大小       44 (0x2c)
      .maxstack  1
      .locals init ([0] object a,
               [1] object b)
      IL_0000:  nop
      IL_0001:  ldc.i4.s   10
      IL_0003:  box        [mscorlib]System.Int32      //对10进行装箱
      IL_0008:  stloc.0    //a指向内容是10的实例
      IL_0009:  ldloc.0
      IL_000a:  stloc.1     //b指向内容是10的实例
      IL_000b:  ldc.i4.s   20     
      IL_000d:  box        [mscorlib]System.Int32     //对20进行装箱
      IL_0012:  stloc.0    //a指向内容是20的实例
      IL_0013:  ldloc.0
      IL_0014:  callvirt   instance string [mscorlib]System.Object::ToString()
      IL_0019:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_001e:  nop
      IL_001f:  ldloc.1
      IL_0020:  callvirt   instance string [mscorlib]System.Object::ToString()
      IL_0025:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_002a:  nop
      IL_002b:  ret
    周雪峰
    2009年12月2日 10:55
    版主