I. C#中值类型和引用类型
1. 类class 引用类型,结构struct值类型
2. 数组是引用类型,即使元素是值类型,int[]是引用类型
3. 枚举是值类型enum
4. 委托类型delegate是引用类型
5. 接口类型interface是引用类型,但可以由值类型实现。
II. 值的表达式:表达式“2+3”的值就是5;而对于引用类型的表达式,它的值是一个引用,而不是该引用所指代的对象,如String.Empty的值不是一个空字符串,而是对空字符串的一个引用。
III. 变量的值在它声明的位置存储,局部变量的值总是存在栈stack中,引用对象的实例总是存放在堆heap中,静态变量也是。
V. 值类型不能派生出其他类型,不需要额外的信息描述值实际是什么类型;而引用类型,每个对象的开头有一个标识对象实际类型的数据块,同时还提供一些其他信息。永远都不能改变对象的类型,执行简单的强制类型转换时,运行时获取一个引用,检查它引用的对象是否为目标类型的一个有效对象,若有效返回原始引用,否则抛出异常。
误解:
误区1:结构是轻量级的类
误区2:引用类型保存在堆上,值类型保存在栈上
引用的实例总是在堆上创建无误。但是一个类中有一个int的实例变量,那个在类的任何对象中,该变量的值总是和对象的其他数据在一起,也就是在堆上。只有局部变量(方法内部声明的变量)和方法的参数在栈上。对于C#2.0及以上,很多局部变量并无完全存放在栈中,详见后续匿名方法。
误区3:对象在C#中默认是通过引用传递的:引用类型作为方法参数传递时,默认是使用“值传递”--这个值是一个引用。
VI. 拆箱和装箱
对于引用类型的变量,它的值永远是一个引用;对于值类型的变量,它的值永远是该值类型的一个值。
int i=5; object o=i; int j= (int)o;
i是值类型的变量,o是引用类型的变量。将i赋值给o,发生装箱:运行时将在堆上创建一个包含值(5)的对象。o的值是该对象的一个引用,该对象的值是原始值的一个副本,改变i的值对该对象的值无影响。
拆箱:o被拆箱成int 类型的值,拆箱无误(即o的值可以转化为int),复制箱内的值给j,拆箱后j和该对象没有任何关系。
拆箱很容易看出来,有强制的类型转化。什么时候会装箱呢?装箱比较隐蔽,通常为一个值类型调用ToString,Equals或GetHashCode方法是,该类型没有覆盖这些方法时,会装箱。GetType()不能被重载,值类型调用时会装箱,所以可以是有typeof代替。另外将一个值类型赋给接口的变量时,也会装箱,如IComparable x =5;
C# in Depth Third Edition 学习笔记-- 值类型和引用,布布扣,bubuko.com