• C# 值类型和引用类型


    一、基本概念

    C#只有两种数据类型:值类型和引用类型

    值类型在线程栈分配空间,引用类型在托管堆分配空间

    值类型转为引用类型称成为装箱,引用类型转为值类型称为拆箱

    以下是值类型和引用类型对照表

    从上图可以简单看出:string,Object,数组,class是引用类型,简单类型,枚举,结构是值类型。

    二、代码展示

    定义一个类和结构调用赋值

    内存分配情况如下图:

    从这张图可以看出,class实例化出来的对象,指向了内存堆中分配的空间;truct实例化出来的对象,是在内存栈中分配。

    修改代码如下:

    内存分配情况:

    由上图可以知:

    object obj=”abc”;  
    string i=(string)obj;  

    值类型和引用类型储的位置不一样
    如果是引用类型,当两个对象指向同一个地方,修改某一个的时候,其它对象的值会发生改变

    注意点:

    1、值类型变量做为局部变量时,该实例将被创建在堆栈上;而如果值类型变量作为类型的成员变量时,它将作为类型实例数据的一部分,同该类型的其他字段都保存在托管堆上。

    2、引用类型变量数据保存在托管堆上,但是根据实例的大小有所区别:当实例的大小小于85000Byte时实例将创建在GC堆上;当实例大小>=85000byte时,则该实例创建在LOH(Large Object Heap)堆上。

    using System;
    
    namespace ConsoleApplication2
    {
        //引用类型(因为‘class’)
        public class SomeRef
        {
            public int x { get; set; }
        }
        //值类型(因为‘struct’)
        public struct SomeVal
        {
            public int x{ get; set; }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                SomeRef r1=new SomeRef();  //在堆上分配
                SomeVal v1 = new SomeVal();//在栈上分配
                r1.x = 5;       //提领指针
                v1.x = 5;       //在栈上修改
                Console.WriteLine(r1.x); //显示5  
                Console.WriteLine(v1.x); //显示5
    
                SomeRef r2 =r1;  //只复制引用(指针)
                SomeVal v2 =v1;  //在栈上分配并赋值成员
                r1.x = 8;       //r1.x和r2.x都会修改
                v1.x = 8;       //v1.x会更改,v2.x不会
    
                Console.WriteLine(r1.x); //显示8  
                Console.WriteLine(r2.x); //显示8  
                Console.WriteLine(v1.x); //显示8
                Console.WriteLine(v2.x); //显示5
    
                Console.ReadKey();
            }
        }
    }
    图片上的代码

     栈stack(先进后出)是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义;

     堆heap(队列优先,先进先出)是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小。

    二、值类型装箱、拆箱

    1、装箱:就是将值类型的数据打包到引用类型的实例中( 比如将int类型的值1赋给object对象obj)

    int  i=1;  
    object obj=(object)i;  

     装箱时到底发生的事情:

    1)、在托管堆中分配内存。分配的内存量是值类型各字段所需的内存量,还要加上托管堆所有对象都有的两个额外成员(类型对象指针和同步块索引)所需的内存量

    2)、值类型的字段复制到新分配的堆内存

    3)、返回对象地址,现在该地址是对象引用;值类型成了引用类型

    2、拆箱:就是从引用数据中提取值类型(比如将object对象obj的值赋给int类型的变量i)

    object obj=”1”;  
    int i=(int)obj;  

    拆箱不是装箱过程倒过来,拆箱的代价要比装箱低的多,拆箱是获取指针的过程,该指针指向包含在一个对象中的原始值类型。

    拆箱后将堆中的字段包含的值复制栈的值类型实例中。

    3、值类型变为引用类型不一定要装箱,例如:

    string str = "joye.net" + 26;   //需要将26装箱为string类型
    string str1 = "joye.net" + 26.ToString(); //tostring后不需要装箱

    感兴趣的可以用IL看一下。

    三、参考资料

      《深入理解C#》第二版

      《CLR VIA C#》第四版

      《C# 高级编程》第四版

      还有很多网络上的文章,就不一一例举了

  • 相关阅读:
    java 自定义异常输出信息(使用构造器)
    idea 项目java版本选项位置
    编译、安装rdesktop 1.8.3
    ubuntu下编译源码 make 出现 make: 'Makefile' is up to date.
    ubuntu 图形化界面 gui 桌面版 root登录 sorry,that didn't work.please try again! 抱歉,认证失败。请重试
    MIUI 10 已连接 但无法访问互联网 的解决方案
    idea中 和outline相似的功能
    idea web项目debug模式实时更新按钮不生效原因
    javaweb学习总结二(静态导入、自动拆装箱、增强for与可变参数)
    javaweb学习总结一(eclipse常用快捷键、debug调试以及junit测试框架)
  • 原文地址:https://www.cnblogs.com/yinrq/p/5588330.html
Copyright © 2020-2023  润新知