前段时间面试,问到值类型和引用类型,虽然能说出个大概,但是对于这两个天天接触的基本类型,还是有点懵懵的,在这里详细写一下两者的区别。

 

CLR支持两种类型:引用类型值类型。  C#的new操作符返回对象内存地址----即指向对象数据的内存地址。

在文档中,任何称为“类”的类型都是引用类型,如,System.Exception类、System.IO.FileStream类等等。相反,所有值类型都称为枚举或结构,如System.Int32结构、System.Boolean等等。

 

其中的规律是:所有从System.ValueType直接派生或者间接派生的都是属于值类型。我们常用的枚举,是从System.Enum抽象类中派生,而System.Enum又是从System.ValueType派生,所以枚举也是一个典型的值类型。

有一点很重要,所有值类型都隐式密封(也就是java中的final),目的是防止将值类型用作其他引用类型或值类型的基类型。

 

值类型相较于引用的类型的好处是性能得到提升。使用引用类型时,需要清楚如下几点:

  1. 内存必须从托管堆分配。
  2. 堆上分配的每个对象都有一些额外成员,这些成员必须初始化。
  3. 对象中的其他字节(为字段而设)总是设为零。
  4. 从托管堆分配对象时,可能强制执行一次垃圾回收。

而值类型的实例一般在线程栈上分配(也可以作为字段嵌入引用类型中),在代表值类型实例的变量中不包含指向实例的指针。由于变量已包含实例的字段,所以操作实例中的字段不需要提领指针,值类型的实例不受垃圾回收器的控制。因此值类型的使用缓解了托管堆的压力,并减少了应用程序生存期内的垃圾回收次数。

 

如下代码代表了创建值类型和引用类型时,内存里发生的细节:

//引用类型(这是一个类)
class SomeRef{public Int x;}

//值类型(这是一个结构)
struct SomeVal{public Int x;}

static void ValueTypeDemo()
{
    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=9;                                 //v1.x会更改,v2.x不变
    Console.WriteLine(r1.x);        //显示8
    Console.WriteLine(r2.x);        //显示9
    Console.WriteLine(v1.x);       //显示9
    Console.WriteLine(v2.x);       //显示5
}

 

 

至此,值类型和引用类型心里也有一定的了解了,当然这对于CLR为我们做的步骤还太少,以后如有 更深理解,再补充吧。