值类型和引用类型的区别

来源:互联网 发布:竣工验收结算软件 编辑:程序博客网 时间:2024/04/28 23:33

直白点儿说:值类型就是现金,要用直接用;引用类型是存折,要用还得先去银行取现。

 

值类型包括:内置值类型、用户自定义值类型、和枚举,如 int,float bool 等,以及struct等。

引用类型包括接口类型、用户自定义的类、委托等。如 string 、DateTime、数组等。

值类型是存储在堆栈中,而引用类型是存储在托管堆上,C#程序首先被编译成IL程序,然后在托管执行。值类型直接从堆栈中里面取值,而引用类型必须要先从堆栈里面取出它的地址,再根据这个地址在堆里找到对应的值。

 

1.    值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址。

2.     效率: 值类型效率高,不需要地址转换;引用类型效率较低,需要进行地址转换

3.     值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用

4.     值类型继承自System.ValueType,引用类型继承自System.Object

5.     栈的内存分配是自动释放;而堆在.NET中会有GC来释放         值类型使用完后立即回收;引用类型使用完后不立即回收,而是交给GC处理回收

6.      值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。
7.类型扩展: 值类型不易扩展,所有值类型都是密封(seal)的,所以无法派生出新的值类型;引用类型具有多态的特性方便扩展。

装箱。那么什么是装箱呢?其实装箱就是值类型到引用类型的转化过程。将一个值类型变量装箱成一个引用类型变量,首先会在托管堆上为新的引用类型变量分配内存空间,然后将值类型变量拷贝到托管堆上新分配的对象内存中,最后返回新分配的对象内存地址。装箱操作是可逆的,所以还有拆箱操作。拆箱操作获取只想对象中包含值类型部分的指针,然后由程序员手动将其对应的值拷贝给值类型变量。接下来我们来看看典型的装箱和拆箱操作。

   1:public static class BoxingAndUnboxing

   2:{

   3:    public static void Demonstration()

   4:     {

   5:        int ageInt = new int();

   6: 

   7:        // Boxing operation.

   8:        object ageObject = ageInt;

   9: 

  10:        //ageObject = null;

  11: 

  12:        // Unboxing operation.

  13:         ageInt = (int)ageObject;

  14: 

  15:         Console.WriteLine(ageInt);

  16:     }

  17:}

在该方法中,我们首先声明了一个值类型变量ageInt,但并未给它赋值,接着声明了一个典型的引用类型变量ageObject,并把ageInt赋给它,这里就进行了一次装箱操作。编译器现在托管堆上分配一块内存空间(空间大小为对象中包含的值类型变量所占空间总和外加一个方法表指针和一个SyncBlockIndex),然后把ageInt拷贝到这个空间中,再返回该空间的引用地址。接下来第13行则是拆箱操作,编译器获取到ageObject对象中值类型变量的指针,然后将其值拷贝给值类型变量。如果你把第10行注释掉的代码打开(这是通俗说法,其实就是取消注释),那么第13行就会抛出System.NullReferenceException异常,要说问什么,这又会牵扯出值类型跟引用类型另一个大的不同。看见了吧,声明ageInt时并没有赋值,如果关掉第10行代码,程序不会报错,最后打印出个0,这说明在声明值类型变量时,如果没有初始化赋值,编译器会自动将其赋值为0,既然值类型没有引用,那么它就不可能为空。引用类型不一样,它可以为空引用,一张过期作废的银行卡是可以存在。而如果将一个空的对象拆箱,编译器上哪儿去找它里面的值类型变量的指针呢?所以这也是拆箱操作需要注意的地方。


 

 

 

原创粉丝点击