说明:本文主要参考和修改下面的文章,向原作者致敬!
http://www.hackdark.com/_NET_C_/2013/0216/3257.html
一、基础概念
.Net的类型分为两种,一种是值类型,另一种是引用类型。
这两个类型的本质区别,值类型数据是分配在栈中,而引用类型数据分配在堆上。那么如果要把一个值类型数据放到堆上,就需要装箱操作;反之,把一个放在堆上的值类型数据取出来,则需要进行拆箱操作。
简单的装箱与拆箱操作语句:
- int i = 123;
- object obj = i;
将i将箱!
- if(obj is int)
- {
- int j = (int)obj; // Unboxing,这里进行了强制类型转换
- }
将obj拆箱!
由图1,在栈中的值类型与在堆中的引用类型的存储方式是不一样的。所以两者相互转换的时候需要进行装、拆箱操作。
二、非泛型集合类的种种问题
1、非泛型集合类的类型转换性能差
装箱与拆箱操作影响效率,主要有两个方面:
一个就是对于堆的操作效率比较低;另一个就是对于堆上分配的内存资源,需要GC来回收,从而降低程序效率。
2、类型安全问题
当装箱与拆箱使用在集合中时,如ArrayList或者HashTable之类,把值类型放到集合中,可能会出现潜在错误。
代码一:
- public interface IPersonName
- {
- string Name { get; set; }
- }
-
-
- public struct SPerson : IPersonName
- {
- private string name;
- public string Name
- {
- get { return name; }
- set { name = value; }
- }
-
- public SPerson(string personName)
- {
- name = personName;
- }
-
- public override string ToString()
- {
- return name;
- }
- }
-
-
-
- static void Main(string[] args)
- {
- ArrayList arrPersons = new ArrayList();
- SPerson p = new SPerson("OldName");
- arrPersons.Add(p);
-
- p = (SPerson)arrPersons[0];
- p.Name = "NewName";
- Console.WriteLine(((SPerson)arrPersons[0]).Name);
-
- ((SPerson)arrPersons[0]).Name = "NewName";
-
- //((IPersonName)arrPersons[0]).Name = "NewName";
- Console.WriteLine(((IPersonName)arrPersons[0]).Name);
- Console.ReadLine();
- }

图3
报的错误是,“无法修改取消装箱转换的结果”.
如上操作不能通过编译。为什么呢,对于“( (Person ) arrPersons[0] )”来说,是系统用一个临时变量来接收拆箱后的值类型数据,那么由于值类型是分配在栈上,那么操作是对实体操作,可是系统不允许对一个临时值类型数据进行修改操作。
上面这段话,我暂时无法很好的理解,请各位发表高见!保留住!
代码二:
- static void Main(string[] args)
- {
- ArrayList arrPersons = new ArrayList();
- SPerson p = new SPerson("OldName");
- arrPersons.Add(p);
- p = (SPerson)arrPersons[0];
- p.Name = "NewName";
-
- Console.WriteLine(((SPerson)arrPersons[0]).Name);
- //((SPerson)arrPersons[0]).Name = "NewName";
- ((IPersonName)arrPersons[0]).Name = "NewName";
- Console.WriteLine(((IPersonName)arrPersons[0]).Name);
- Console.ReadLine();
- }
图4
按代码二的写法,是通的。什么原因?
产生的临时变量的类型为IPersonName,属于引用类型,那么相当于临时变量就是原对象的引用,那么对于对于它的修改会直接修改到原对象,因此是可以的。可以说这里的不同本身在于产生临时对象的类型不同,从而造成本质的区别。
通过接口来改写,这样就减少了装箱和拆箱操作,同时也保证了修改的正确性。不过要注意的是,这里接口对于的是引用类型,如果接口访问的或者返回的是值类型,那么用接口虽说能实现了,但是对于装箱和拆箱操作来说,并没有减少。
参考文档
http://blog.csdn.net/ycyangcai/article/details/3151767
http://www.hackdark.com/_NET_C_/2013/0216/3257.html
http://www.cnblogs.com/gaobanana/archive/2010/03/04/1678312.html