• 装拆箱


    装箱(boxing)和拆箱(unboxing)是C#类型系统的核心概念。

    值类型继承至System.ValueType,引用类型继承至System.Object,详细的类继承关系如下图所示:

    值类型实例通常分配在线程的栈(stack)上,并且不包含任何指向实例数据的指针,因为变量本身就包含了其实例数据,而引用类型实例分配在托管堆(managed heap)上,变量保存了实例数据的内存引用.栈是一种先进后出,并且由系统自动操作的存储空间,而堆(在.NET上准确的说是托管堆Managed Heap)是一种自由储存区(Free Memory),在该区域中,必须明确的为对象申请存储空间(使用的new关键字):示例:
     public void Method() 
    
    { 
    
    int x=3;  //第一行
    
    int y=5;  //第二行
    
    class cls1 = new class();  //第三行
    
    }

    下面逐行分析以上三行代码的执行过程。

    1行:当这行代码执行时,编译器为它分配一小块栈内存,运行时栈负责提供程序所需的内存。

    2行:栈在第一块内存的顶部分配了另一块内存。注意,内存的分配与释放遵循后进先出逻辑。

    3行:编译器在栈上创建了一个指针,真实的对象存储在托管堆中。托管堆并不跟踪运行内存,它更像一堆随时可以访问的对象。托管堆上分配的内存大小由具体对象的大小而决定。

     

    不扯了,说下装拆箱:

    1、装箱就是将一个值类型转换成等价的引用类型。 它的过程分为这样几步:
    1) 在托管堆上分配一个新的对象。
    2) 将栈上的数据转移到托管堆新创建的对象空间上。
    3) 在栈上分配一个指向托管堆的引用。
    2、拆箱则是将一个已装箱的引用类型转换为值类型:
    1)将托管堆上对象的数据转移到栈上
    2) 垃圾回收器(GC)回收托管堆上的内存空间。
    3)删除栈上分配的对象引用。
     
    1             int n = 100;
    2             //有没有发生装箱?没有,只是调用方法.查看IL没有box,unbox关键字
    3             string s = Convert.ToString(n);
    4             int m = int.Parse(s);
    5             Console.ReadKey();
    View Code

    装拆箱:

    1             int n = 100;
    2             object o = n;//发生了一次装箱
    3             int m = (int)o; //发生了一次拆箱
    4             Console.WriteLine(m);
    5             Console.ReadKey();
    View Code

    查看IL代码如下:

    注意问题:装箱的时候是什么类型,拆箱的时候也必须使用对应的类型拆箱。否则会报错

    =============================================================================================

    之前写了一篇比较是否为同一对象(比较是否为同一对象),object.ReferenceEquals如果两个参数都是值类型会是怎样的呢?

    1            int n = 100;
    2             int m = 100;
    3             Console.WriteLine(n.Equals(m));
    4             Console.WriteLine(object.ReferenceEquals(n, m));
    5             Console.ReadKey();
    View Code

    运行结果是false,我们来查看下IL代码,很明显,n和m都装箱变为了引用类型,它们所指向的地址不一样,无论如何都是不成立的

    =================================================================================

    拼接字符串也会涉及装箱问题:比如:

    1             int n = 10;
    2             object o = n; //装箱一次
    3             n = 100;
    4             Console.WriteLine(n + "," + (int)o); //装箱两次,拆箱一次
    5             Console.ReadKey();
    View Code

    还是得查看一下IL代码:因为拼接字符串是调用string.Concat(object,object,object)这个方法,每拼接一个字符串会把int隐式装箱再连接

    ======================================================================

    装箱和拆箱性能问题

    使用ArrayList:

     1             ArrayList arrInt = new ArrayList();
     2             Stopwatch watch = new Stopwatch();
     3             watch.Start();
     4             for (int i = 0; i < 1000000; i++)
     5             {
     6                 arrInt.Add(i);
     7             }
     8             watch.Stop();
     9             Console.WriteLine(watch.Elapsed);
    10             Console.ReadKey();
    View Code

    运行结果:

    使用泛型:

     1     //使用泛型集合避免装箱和拆箱。
     2             List<int> arrInt = new List<int>();
     3             Stopwatch watch = new Stopwatch();
     4             watch.Start();
     5             for (int i = 0; i < 1000000; i++)
     6             {
     7                 arrInt.Add(i);
     8             }
     9             watch.Stop();
    10             Console.WriteLine(watch.Elapsed);
    11             Console.ReadKey();
    View Code

    运行结果:

    这可以看出装拆箱还是比较影响性能的

  • 相关阅读:
    java基础16 捕获、抛出以、自定义异常和 finally 块(以及关键字:throw 、throws)
    java基础15 内部类(成员内部类、局部内部类)和匿名内部类
    java基础14 多态(及关键字:instanceof)
    java基础13 接口(及关键字:interface、implements)
    Java 线程控制
    Java 多线程创建和线程状态
    Java New IO
    Java IO流
    Java 集合和泛型
    Java 动态代理
  • 原文地址:https://www.cnblogs.com/entclark/p/7600523.html
Copyright © 2020-2023  润新知