1.一个面向对象的语言为什么要有值类型

值类型与引用类型基于oop思想发展而来。它抛弃了所有类型都是对象(java)设计思路,这种设计思路看上去简单暴力,但是对编译器的性能增加了很大瓶颈。C#将负责计算的类型剥离出来设置为值类型,将负责抽象化的设置为引用类型。与C++中单纯的内置类型不同,C#中的值类型由于专门负责处理计算,还可以拥有构造器、终结器、方法等一系列类中才能有的成员,一切都是为了处理计算服务。

2.从内部来看值类型与引用类型

值类型储存在栈中。引用类型分为两部分储存,一部分在栈中存变量名和地址,另一部分在堆中存成员。string虽然是引用类型,但是它却存在栈中。

堆中的部分是托管的(类似JVM),栈中的遵守先进后出。

值类型也是继承自ValueType(继承自Object),但是它覆盖了object中的方法,编译器会对继承自ValueType的对象特殊处理。

3. 装箱

由于值类型实际上也是继承自Object,所以能隐式转换为Object,这就导致了装箱的问题出现。

int aq = 10;
object bq = aq;//装箱
int cq = (int)bq;//拆箱

Console.WriteLine("aq={0},bq={1},cq={2}", aq, bq, cq);
//aq=10,bq=10,cq=10
aq=15;
Console.WriteLine("aq={0},bq={1},cq={2}", aq, bq, cq);
//aq=15,bq=10,cq=10
Console.WriteLine(bq.GetType().Name);
//Int32

由上可知,在这个隐式转换的过程中,转换后的object和之前的值类型没有关系了。这是因为在转换的时候,编译器会在堆上创建一个引用类型,类型名也是Int32,内部property装的是aq的值。
拆箱的时候又会在栈上创建一个值类型,将property的值赋过去。

频繁的在栈、堆上创建对象是很浪费空间时间,所以要避免装箱拆箱的情况出现,尤其是循环和递归函数中。值类型与引用类型的传递应使用方法或泛型。