• 装箱和拆箱


      今天看书,觉得这个例子很有意思,记录一下,mark~

      首先放上例子

       1:  using System;
       2:  using System.Collections.Generic;
       3:  using System.Linq;
       4:  using System.Text;
       5:   
       6:  namespace Boxing {
       7:      class Program {
       8:          interface IChange {
       9:              void Change(int x, int y);
      10:   
      11:          }
      12:          struct Point : IChange {
      13:              int x, y;
      14:              public Point(int x, int y) {
      15:                  this.x = x;
      16:                  this.y = y;
      17:              }
      18:   
      19:              public void Change(int x, int y) {
      20:                  this.x = x;
      21:                  this.y = y;
      22:              }
      23:   
      24:              public override string ToString() {
      25:                  return String.Format("({0},{1})", x, y);
      26:              }
      27:          }
      28:          static void Main(string[] args) {
      29:              Point p = new Point(1, 1);
      30:   
      31:              Console.WriteLine(p); //box
      32:              p.Change(2, 2);  //由于已经装箱 由此函数传参是引用类型的值传递了,传地址,改变p
      33:              Console.WriteLine(p);
      34:   
      35:              object o = p; //box 
      36:              p.Change(3, 3);
      37:              Console.WriteLine(p);
      38:   
      39:              ((Point)o).Change(4, 4);//unbox 函数的传参方式为值类型的值传递了,传复制的一个值不改变p
      40:              Console.WriteLine(p);
      41:   
      42:              ((IChange)p).Change(5, 5);//box 更改装箱对象 然后丢弃 准备GC过程
      43:              Console.WriteLine(p);
      44:   
      45:              ((IChange)o).Change(6, 6);//更改装箱对象,这是唯一可以从外部改变值类型的方式
      46:              Console.WriteLine(o);
      47:   
      48:              Console.ReadLine();
      49:          }
      50:      }
      51:  }

      对于输出结果,相信不少人如果不熟悉的话还是会弄错的~

    image

      判断还是很简单的,只要想对值类型的一个实例引用,该实例就必须装箱.假如方法要求的参数是引用类型,就会发生装箱~简单的分析:

        .maxstack 3
        .entrypoint
        .locals init (
            [0] valuetype Boxing.Program/Point p,
            [1] object o,
            [2] valuetype Boxing.Program/Point CS$0$0000
        )
     
        //第一次装箱是因为方法参数要求Object是引用类型
        IL_000b: ldloc.0
        IL_000c: box Boxing.Program/Point
        IL_0011: call void [mscorlib]System.Console::WriteLine(object)
        ...
        IL_0017: ldloca.s p //推送p的地址到堆栈 这样就可以改变p
           //调用已经改变了的p
        IL_0021: ldloc.0
        IL_0022: box Boxing.Program/Point
        IL_0027: call void [mscorlib]System.Console::WriteLine(object)
        IL_002c: nop
        IL_002d: ldloc.0
        IL_002e: box Boxing.Program/Point
        ...
        IL_003e: ldloc.0
        IL_003f: box Boxing.Program/Point
        IL_0044: call void [mscorlib]System.Console::WriteLine(object)
        ...
        IL_004a: ldloc.1
        IL_004b: unbox.any Boxing.Program/Point
        IL_0050: stloc.2
        IL_0051: ldloca.s CS$0$0000//拆箱过程p给复制到一临时变量 因此p不受影响
        ...
        IL_005b: ldloc.0
        IL_005c: box Boxing.Program/Point
        IL_0061: call void [mscorlib]System.Console::WriteLine(object)
        ...
        IL_0067: ldloc.0

    IL_0068: box Boxing.Program/Point//类型转为接口类发生box

    //方法完成后将该返回值推送到堆栈上 但是没有返回值 因此方法结束后已装箱对象准备垃圾回收
        IL_006f: callvirt instance void Boxing.Program/IChange::Change(int32, int32)

        ...
        IL_0075: ldloc.0
        IL_0076: box Boxing.Program/Point
        IL_007b: call void [mscorlib]System.Console::WriteLine(object)
        IL_0080: nop
        IL_0081: ldloc.1
        IL_0082: castclass Boxing.Program/IChange//这里发生了引用对象转为类
        ...
        IL_008f: ldloc.1
        IL_0090: call void [mscorlib]System.Console::WriteLine(object)
        

      由此可知,只有接口可以从外部改变值类型的字段!另外,未装箱的对象调用继承或者重新的虚方法(Tostring,GetType,GetHashCode…)也会发生装箱.常见发生转换的时候:[参见 《C#规范 4.0》]

    · 从任何 value-type 到 object 类型。

    · 从任何 value-type 到 System.ValueType 类型。

    · 从任何 non-nullable-value-type 到 value-type 实现的任何 interface-type。

    · 从任何 nullable-type 到由 nullable-type 的基础类型实现的任何 interface-type

    · 从任何 enum-type 到 System.Enum 类型。

    · 从任何具有基础 enum-type 的 nullable-type 到 System.Enum 类型。

      请注意,对类型形参进行隐式转换将以装箱转换的形式执行(如果在运行时它最后从值类型转换到引用类型)

    总结:

    1. 只有接口可以从外部改变值类型的字段
    2. 只要想对值类型的一个实例引用,该实例就必须装箱  
  • 相关阅读:
    R.java文件介绍
    Windows CE Notification API的使用方法
    Vue.js组件的重要选项
    Vue.js如何划分组件
    细数那些年我用过的前端开发工具
    前端几个常用简单的开发手册拿走不谢
    Bootstrap相关优质项目学习清单
    前端页面实现报警器提示音效果
    人工智能面试题86问,新手找工作必备!
    人工智能面试题86问,新手找工作必备!
  • 原文地址:https://www.cnblogs.com/lijinfeng042/p/2225031.html
Copyright © 2020-2023  润新知