C# 值类型和引用类型

   有一段讲解值类型和引用类型的段落很好。摘抄自一本书内,如下:

  在分析C#中的值类型和引用类型之前,讲两个例子来使抽象的概念变得具体。

  假设你在看一本书,你的朋友此时也想看你手上的那本书。为了让朋友看到,要么将自己受伤的书借给对方,要么再复制一本给他。无论如何做,都是对书本身进行操作,而复制后的两本书,完全是独立的,没有任何的关系。这种行为就可以类比C#中的值类型的行为。

  换另一种假设,假设你正在看电视,换台、调音量等对电视的操作都通过使用电视遥控器,而不是直接对电视操作。若你的朋友要看别的电视台或者改变音量,无需把电视给他,只需要把遥控器给他就好了。这就是引用类型的行为。

C#中的大部分是引用类型,但在实际的开发过程中,值类型还是常用的。引用类型总是从托管堆分配,C#要求所有对象都使用new 操作符创建。

区别:最大的区别是,基于值类型的变量直接包含值。也就是说将一个值类型变量赋给另一个值类型变量时,将直接复制其包含的值,引用类型变量的赋值只是复制对象的引用。举一个栗子,若所有类型都是引用类型的,那么哪怕是一个数字都是引用类型的,也就是说使用一个数字1都是需要进行一次消耗巨大的内存分配。

  另一个差别是,值类型不能派生其他的类型,所有值类型都是隐式密封的,这么做的目的是为了防止将值类型用作其他类型的基类型。

  变量的值是如何分配的?通常说,变量的值分配的位置与声明该变量的位置有关。局部变量的值总是存储在线程栈上,实例变量的值和实例本身存储在实例存储的地方。引用类型实例和静态变量总是存储在堆上。

  值类型的实例一般是分配在线程栈上而不是全部,是由于有些情况下,值类型也有可能分配在托管堆上。

这些特殊情况包括数组中的元素,引用类型中的值类型字段,迭代器块中的局部变量,闭包情况下匿名函数(Lamda)中的局部变量。此时,有可能出现的情况是,若该情况下的值类型实例如分配在线程栈上,有可能会出现线程栈中的方法已经调用结束,但还是会访问这些值的情况,值会随着调用方法的返回而被清除掉。因此它们被分配在托管堆上,以满足在方法返回之后还能被访问的要求。

  因此,单纯的说“引用类型保存在托管堆上,值类型保存在线程栈上”是不准确的。准确来说,引用类型总是分配在托管堆上,但值类型并非总是分配在线程栈上。

  任何被称为“类”的类型都是引用类型。特别注意的是,字符串string并非是一个实际的字符串,而是对字符串的一个引用,故而字符串是引用类型。

  C#中常见的引用类型,如System.Collections.Generic.List类、System.Text.Decoder类。

  与引用类型相对的就是值类型,可分为结构和枚举两种。

  结构又分为三种:

  • 数字型结构:System.Int32结构、System.Float结构、System.Decimal结构等。
  • 布尔型结构:常见即System.Boolean等。
  • 用户自定义的结构。

根据微软官方的语言规范文档,所有的值类型都必须派生自System.VlaueType。另,值类型有两种表示方式,分别是未装箱和已装箱。而装箱机制就是☞将值类型转换成引用类型。是由于值类型不在托管堆中分配,不被垃圾回收。但很多情况下,需要获取和操作对值类型实例的引用,此时装箱机制应运而生。

猜你喜欢

转载自www.cnblogs.com/allyh/p/9164973.html