zoukankan      html  css  js  c++  java
  • CLR:基元类型、引用类型和值类型

     

    最新更新请访问: http://denghejun.github.io

     

    前言

    今天重新看了下关于CLR基元类型的东西,觉得还是有必要将其记录下来,毕竟这是理解CLR成功

    之路上的重要一步,希望你也和我一样。

     

    基元类型

    编译器直接支持的数据类型称之为基元类型,针对那些程序员自定义的类型而言。所有基元类型

    直接映射到FCL(Framework class library)中存在的类型;比如C#中int直接映射到System.

    Int32类型,且在编译为IL(中间语言)时,他们将会是一模一样的:

    int a=0;
    System.Int32 a=0;
    int a=new int();
    System.Int32 a=new System.Int32();


    至于,为什么我们经常使用的是int这样的简单类型,而不是System.Int32,微软在C#语言规范

    (CLS)中有这样的建议:“从风格上说,最好是使用关键字,而不是使用完整的系统类型名称”。

    但是使用关键字有时候会使得程序员倍感迷惑,例如int比较没有Int32那样直接的显示这是一个有

    符号的32值。

     

    引用类型

    任何称之为“类”的类型都是引用类型。引用类型总是从堆上分配内存,C#的new操作符将会返回对

    象的内存地址。使用引用类型时必须考虑以下事实;

      • 内存必须从托管堆上分配
      • 堆上分配的每个对象都有一些额外的成员,它们必须初始化
      • 对象中的其他字节总是设为零
      • 从托管堆上分配一个对象时,可能强制执行一次垃圾收集操作

    很明显,过多的使用引用类型可能会导致应用程序性能显著下降。

    引用类型变量的互相赋值只会赋值对象的内存地址,所以指向同一对象的变量在发生改变时实际上影

    响的是同一个对象。

     

    值类型

    所有值类型又都称之为结构枚举。值类型在线程栈上分配空间。所有的值类型都直接派生于抽象类

    System.ValueTye,而后者本身又直接从System.Object派生。所有值类型都是密封的(sealed

    ,因此,无法被继承,从而无法使用值类型定义新的类型。

    值类型变量的互相赋值将会执行一次逐字段的复制。

    值类型与引用类型的取舍

    将数据类型定义为结构(值类型)需要考虑一下几点:

      • 不需要从其他类型继承
      • 不需要派生
      • 类型实例较小或不作为实参和返回值
      • 类型实例不需要做线程同步访问

    无法继承和派生是值类型的显著特点,你必须慎重考虑他们。另外,若值类型实例过大,在入参时会

    发生复制行为,占用空间;在作为返回值时也将值类型的实例复制到调用者的分配内存中。因为未装箱

    的值类型没有同步索引块,所以不能使用Monitorlock等方法(语句)让多个线程同步对这个对象的

    访问。

    装箱与拆箱

    值类型有两种表现形式:未装箱(unboxed)和已装箱(boxed)形式;引用类型总是处于已装箱模

    式。

    值类型是一种“轻型”的类型,它不会作为对象在托管堆中分配内存,不会被垃圾回收,也不能通过指针

    来引用。但在许多情况下我们需要获取对一个值类型实例的引用:

    struct Point
    {
    public int x,y;
    }
    
    ArrayList list=new ArrayList();
    Point p;
    for(int i=0;i<5;i++)
    {
    p.x=p.y=i;
    list.Add(p); // 将值类型进行装箱,并添加到集合中
    }

    上面的例子中,由于ArrayListAdd方法需要一个类型为Objec的入参,而我们传入的是值类型Point

    ,所以这里将发生装箱的操作。所有在值类型转化为引用类型的地方都需要装箱。装箱(boxing)内部

    发生的过程如下:

      1.在托管堆上分配好内存,大小为值类型所有字段的大小加上引用类型的额外

       成员(对象指针和同步块索引)

      2.值类型的字段复制到新的堆内存中

      3.返回对象的地址

    可见,已装箱的值类型的生命周期超过了未装箱的值类型。

    另外,值类型在转化为某个接口或调用未重写的基类方法时(所有的值类型都继承System.Object),需

    要装箱。因为基类的this希望接受一个指向堆上的一个对象的指针。

    拆箱并不是装箱的逆过程:

    Point p=(Point)list[0];

    拆箱在CLR中分两步完成这个操作:

      1.获取已装箱的Point对象中的各个Point字段的地址,这个过程就是拆箱(unboxing);

      2.将这些字段包含的值从堆中复制到基于栈的值类型实例中(也就是上例中的p)。

    所以,拆箱实际上是指一个寻址的过程,拆箱的代价远低于装箱,因为它确实知识一个简单的寻找指针

    的过程而已,在这之后才会发生逐字段复制的过程。

  • 相关阅读:
    mini2440烧写nor flash
    mini2440系统引导(五)串口UART
    mini2440系统引导(四)存储控制器
    mini2440系统引导(三)当前状态寄存器CPSR
    mini2440系统引导(二)中断寄存器
    mini2440系统引导(一)看门狗
    filebeat版本问题导致logstash无法处理接收到的日志
    kernel: possible SYN flooding on port 80. Sending cookies
    grok正则
    【转载】kafka 基础知识
  • 原文地址:https://www.cnblogs.com/denghejun/p/3647677.html
Copyright © 2011-2022 走看看