前段时间面试,问到值类型和引用类型,虽然能说出个大概,但是对于这两个天天接触的基本类型,还是有点懵懵的,在这里详细写一下两者的区别。
CLR支持两种类型:引用类型和值类型。 C#的new操作符返回对象内存地址----即指向对象数据的内存地址。
在文档中,任何称为“类”的类型都是引用类型,如,System.Exception类、System.IO.FileStream类等等。相反,所有值类型都称为枚举或结构,如System.Int32结构、System.Boolean等等。
其中的规律是:所有从System.ValueType直接派生或者间接派生的都是属于值类型。我们常用的枚举,是从System.Enum抽象类中派生,而System.Enum又是从System.ValueType派生,所以枚举也是一个典型的值类型。
有一点很重要,所有值类型都隐式密封(也就是java中的final),目的是防止将值类型用作其他引用类型或值类型的基类型。
值类型相较于引用的类型的好处是性能得到提升。使用引用类型时,需要清楚如下几点:
- 内存必须从托管堆分配。
- 堆上分配的每个对象都有一些额外成员,这些成员必须初始化。
- 对象中的其他字节(为字段而设)总是设为零。
- 从托管堆分配对象时,可能强制执行一次垃圾回收。
而值类型的实例一般在线程栈上分配(也可以作为字段嵌入引用类型中),在代表值类型实例的变量中不包含指向实例的指针。由于变量已包含实例的字段,所以操作实例中的字段不需要提领指针,值类型的实例不受垃圾回收器的控制。因此值类型的使用缓解了托管堆的压力,并减少了应用程序生存期内的垃圾回收次数。
如下代码代表了创建值类型和引用类型时,内存里发生的细节:
//引用类型(这是一个类)
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为我们做的步骤还太少,以后如有 更深理解,再补充吧。