标签:
值类型:声明一个值类型变量,会在栈上分配一个空间,空间里存储的就是变量的值
引用类型:声明一个引用类型变量,会在栈中分配一个空间,存储一个引用,这个引用指向了一个托管堆。
值类型:struct,枚举,数值类型,bool类型
引用类型:数组,类,接口,委托(delegate),Object,string
可以看下下面的例子
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public class ReferenceAndValue
{
public static void Main()
{
Person zerocool = new Person { Name = "ZeroCool", Age = 25 };
Person anders = new Person { Name = "Anders", Age = 47 };
int age = zerocool.Age;
zerocool.Age = 22;
Person guru = anders;
anders.Name = "Anders Hejlsberg";
Console.WriteLine("zerocool‘s age:\t{0}", zerocool.Age); //22
Console.WriteLine("age‘s value:\t{0}", age); //25
Console.WriteLine("anders‘ name:\t{0}", anders.Name); //Anders Hejlsberg
Console.WriteLine("guru‘ name:\t{0}", guru.Name); //Anders Hejlsberg
Console.ReadKey();
}
}
代码分析:
我们首先定义了一个Person类,包含2个属性,Age和Name,其中Age是值类型,Name是string引用类型
然后初始化了2个Person类,zerocool和anders,并赋值,然后再修改Age和Name
这个时候,由于值类型的特点,我们只是在栈上给zerocool.age分配了一段资源,和后来声明的age是没有任何关系的。所以age是不变的。
修改name则不一样,因为是修改引用类型。name和anders.Name是相关联的,所以也会跟着变化。
所以也就会有后来的输出结果。
再说说 装箱 & 拆箱
装箱发生在 值类型 往 引用类型 转换
拆箱发生在 引用类型 往 值类型 转换
将值类型转换为引用类型,需要进行装箱操作(boxing):
1、首先从托管堆中为新生成的引用对象分配内存。
2、然后将值类型的数据拷贝到刚刚分配的内存中。
3、返回托管堆中新分配对象的地址。
可以看出,进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。
将引用内型转换为值内型,需要进行拆箱操作(unboxing):
1、首先获取托管堆中属于值类型那部分字段的地址,这一步是严格意义上的拆箱。
2、将引用对象中的值拷贝到位于线程堆栈上的值类型实例中。
经过这2步,可以认为是同boxing是互反操作。严格意义上的拆箱,并不影响性能,但伴随这之后的拷贝数据的操作就会同boxing操作中一样影响性能。
再举个平时常用的例子
//当我们如下时:
for (int i = 0; i < arr.Length; i++)
{
//
}
//我们更因该这样:
int L = arr.Length;
for (int i = 0; i < L; i++)
{
//
}
i <arr.Length的时候,实际上就进行了装箱,如果重复很多很多次,那就会有内存损耗,所以可以先装箱,再循环。
也可以通过重载函数来避免,通过泛型来避免装箱(暂时还没理解)
像Console.WriteLine()这种方法,也很可能会涉及到装箱等等操作
相关一些比较好的介绍和参考:
http://www.cnblogs.com/huashanlin/archive/2007/05/16/749359.html
http://www.cnblogs.com/hunts/archive/2007/01/19/boxing_unboxing.html
标签:
原文地址:http://www.cnblogs.com/kykstyle/p/4798943.html