zoukankan      html  css  js  c++  java
  • 深入理解类型

    什么是值类型和引用类型

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

    值类型与引用类型的区别

    • 两类型的数据存储位置不同
    • 在引用类型中嵌套值类型时,或者在值类型装箱的情况下,值类型的实例就会被分配到托管堆上
    • 值类型继承自ValueType,ValueType又继承自System.Object;而引用类型则直接继承于System.Object;
    • 值类型的内存不受GC控制,作用域结束时,值类型会被操作系统自行释放
    • 若值类型为密封的(sealed),你将不能把值类型作为其他任何类型的基类;而引用类型则一般具有继承性,这里是指接口和类;
    • 值类型不能为null值,它会被默认初始化为该值类型的默认值;而引用类型默认会初始化为null值,表示不指向托管堆中的任何地址;对值为null的引用类型的任何操作都会引发NullRefernceException异常
    • 由于值类型变量包含其实际数据,因此默认情况下,值类型之间的参数传递不会影响变量本身;而引用类型变量保存的是数据的引用地址,它们作为参数传递时,参数会发生改变,从而影响引用类型变量的值

    几种类型嵌套情况

    • 引用类型中嵌套定义值类型
      • 如果类的字段类型是值类型,它将作为引用类型实例的一部分,被分配到托管堆中,但那些作为局部变量(如下代码中的c变量)的值类型,则仍然会被分配到线程堆栈中.
      • 代码示例
        //引用类型嵌套定义值类型的情况
        public class NestedValueTypeInRef
        {
            //valuetype作为引用类型的一部分被分配到托管堆上
            private int valuetype = 3;
            public void method()
            {
                //C被分配到线程堆栈上
                char c = 'c';
            }
        }
        class Program
        {
    
            static void Main(string[] args)
            {
    
                NestedValueTypeInRef typeInRef = new NestedValueTypeInRef();
            }
        }
    
    - 示例图
    
    ![引用嵌套值类型.png](https://upload-images.jianshu.io/upload_images/2981616-3a77c91cee879a03.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/240)
    
    • 值类型中嵌套定义引用类型

      • 堆栈上将保存该引用类型的引用,而实际的数据则依然保存在托管堆中
      • 代码示例
       public class TestClass
      {
          public int x;
          public int y;
      }
      class Program
      {
          //值类型嵌套定义引用类型的情况
          public struct NestedRefTypeInValue
          {
              //结构体字段,注意,结构中的字段不能被初始化
              private TestClass classinValuetype;
      
              //结构体中的构造函数,注意,结构体中不能定义无参的构造函数
      
              public NestedRefTypeInValue(TestClass t)
              {
                  if (t==null)
                  {
                      throw new ArgumentNullException("t");
                  }
                  classinValuetype = t;
                  classinValuetype.x = 3;
                  classinValuetype.x = 5;
      
              }
          }
          static void Main(string[] args)
          {
              //值类型变量
              NestedRefTypeInValue typeInValue = new NestedRefTypeInValue(new TestClass());
          }
      }
      
      • 示例图

      值类型嵌套引用类型.png

    两大类型间的转换------装箱与拆箱

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

    • 隐式转换;由低级别类型向高级别类型的转换过程(例如子类隐式转换为父类)

    • 显式转换(也称为强制类型转换);这种转换可能会导致精度损失或者出现运行时异常;

      • 转换格式
      (type)(变量,或函数);
      
    • 通过isas运算符进行安全的类型转换;

    • 示例从内存角度对装箱,拆箱进行深入分析

      • 代码示例
      int i=3;
      //装箱
      object o=i;
      //拆箱
      int y=(int)o;
      
      • 装箱步骤:
        • 1,内存分配:在托管堆中分配好内存空间以存放复制的实际数据;
        • 2,完成实际数据的复制:将值类型实例的实际数据复制到新分配的内存中
        • 3,地址返回:将托管堆中的对象地址返回给引用类型的变量
      • 装箱示例图
        装箱过程.png
      • 拆箱步骤:
        • 1,检查实例:首先检查要进行拆箱操作的引用类型变量是否为null,如果为null则抛出异常;反之则检查变量是否和拆箱后的类型是同一类型,若为否,会导致InvalidCasetException异常;
        • 2,地址返回:返回已装箱的实际数据部分的地址
        • 3,数据复制:将托管堆中的实际数据复制到栈中
      • 拆箱示例图
        装箱过程.png

    参数传递问题剖析

    参数可分为形参和实参两种.形参指的是被调用方法中的参数,也就是方法中定义的参数;实参指的是调用方法时,传递给对应参数的值;

    static void Main(string[] args)
    {
        int addNum=1;
        //addNum 就是实参
        Add(addNum);
    }
    //addnum就是形参
    private static void Add(int addnum)
    {
        ........
    }
    
    
    • 值类型参数的按值传递
      • 传递的是该值类型实例的一个副本,因此方法中对参数的改变不会影响到实参
    • 引用类型参数的按值传递
      • 当传递的参数是引用类型时,传递和操作的目标是指向对象的地址,而传递的内容是对象地址的复制.由于地址指向的是实参的值,方法对地址进行操作时,实际上操作了地址所指向的值,所以调用方法后原来实参的值就会被修改
      • string具有不可变性,因此不会改变; 这是一特殊情况
    • 值类型,引用类型参数的按引用传递
      • 使用ref,out关键字来实现参数的传递都是引用传递
  • 相关阅读:
    【BZOJ4517】排列计数(排列组合)
    【BZOJ2733】永无乡(线段树,启发式合并)
    【BZOJ1237】配对(贪心,DP)
    【BZOJ1492】货币兑换Cash(CDQ分治)
    CDQ分治模板
    【BZOJ3932】任务查询系统(主席树)
    【BZOJ3295】动态逆序对(BIT套动态加点线段树)
    【BZOJ3626】LCA(树上差分,树链剖分)
    图书管理系统
    树集合,树映射
  • 原文地址:https://www.cnblogs.com/youMo/p/10360404.html
Copyright © 2011-2022 走看看