发布时间:2016-08-31责任编辑:朱明 浏览:1587
一、值类型与引用类型的区别
在C#中,变量分为两种类型,分别是值类型(ValueType)与引用类型(ReferenceType)。值类型变量直接存储值,即在内存中直接保存它自身的值。例如:
int a = 2;
值类型变量a,在内存中的某处保存了它自身的值:2。引用类型变量存储的是对象的引用(地址),即在内存中保存了一个指向它处的引用,在该地址存储了引用所指的对象。例如:
public class Example
{
//...
}
Example example = new Example();
引用类型变量example,对它进行声明时,会在内存中分配一块空间保存它的内容,而我们所使用的变量example,它实际上是一个指向自身内容的引用。对于如何判断一个变量是值类型还是引用类型,我们稍后会谈到。接下来,让我们了解一下在C#中值类型与引用类型的变量都有哪些区别,这些区别能够帮助我们分辨一个变量是值类型,还是引用类型。
1、值类型变量不能为空,而引用类型变量可以为空(null)。因为值类型变量直接保存它自身的值,并且它总是包含一个值(有默认值存在),所以不能为空。引用类型变量保存的是一个指向它处的引用,当它为空时,表示该变量未指向内存中的任何地方。
2、引用类型直接继承自Systen.Object类,而值类型直接继承自System.ValueType类。System.Object类作为所有类型的基类,它提供了一组方法,这些方法在所有类中都能找到,例如ToString()等。System.ValueType类继承了System.Object类,它并没有添加任何新成员,只是覆盖了所继承的一些方法,使其更适合于值类型。我们可以通过Type.IsValueType属性来判断一个类型是否为值类型。例如:
Example example = new Example();
if (example.GetType().IsValueType)
{
//...
}
else
{
//...
}
3、引用类型变量和值类型变量,它们在内存中的存储分配规则不同。引用类型变量总是分配在堆中,值类型变量可能分配在堆中,也可能分配在栈中,这与它们被声明的位置相关。若值类型变量作为类的字段被声明,那么它将跟随其所属的对象被分配到堆中;若值类型变量作为方法内的本地变量被声明,那么它将存储在栈上。例如:
public class Example
{
int a;//a是值类型变量,它作为Example类的字段被声明,将跟随引用类型对象被分配,位于堆上。
public void Test()
{
int b = 1;//b是值类型变量,它作为Test方法的本地变量,位于栈上。
}
}
Example example = new Example();
4、所有的值类型都是密封的(seal),无法派生出新类型。而引用类型则可以继续派生。这也是值类型适合存储数据,引用类型适合定义行为的原因。以下是C#中值类型与引用类型的详细划分列表:
作为值类型的数据类型 | 作为引用类型的数据类型 |
bool | class |
byte | object |
char | string |
decimal | interface |
double | delegate |
float | |
int | |
long | |
short | |
sbyte | |
uint | |
ulong | |
ushort | |
enum | |
struct |
二、值类型、引用类型变量与内存中堆和栈的关系
我们在上面谈到,引用类型变量与值类型变量在内存中具有不同的分配规则,它们涉及到内存中堆与栈的使用问题,对代码的执行效率有不小的影响。下面我们就堆与栈的问题做一番说明。
堆(Heap)与栈(Stack),是内存中的不同区域。在内存中栈负责保存代码的执行路径(调用路径)。当我们的代码开始调用一个方法时,首先将一段指令放置到栈上,接下来放置方法的参数。然后当代码执行到方法体中声明变量的位置,这里会根据变量为值类型还是引用类型,体现出差别。
第一种情况,变量为值类型。那么它将被进栈置栈顶,存放在栈上。当方法执行结束,结果被返回,此时该方法在栈上分配的所有空间都被清空,程序将自动回到栈上最初调用方法的位置。这就告诉我们,栈是由内存自行维护的,不存在垃圾收集的问题。
第二种情况,变量为引用类型。那么它将在堆上被创建,同时在栈上生成一个指向这个堆的指针。当方法执行结束后,栈上的相关内容被清空,此时在堆中将剩下孤零零的引用类型变量。这就产生了垃圾,需要靠垃圾回收机制来收回这部分空间。垃圾回收是非常消耗性能的,所以对于堆和栈的使用,我们需要特别注意。
战国工作室 供稿