.NET 1.0创建整数集合的方法(例如)是:
ArrayList list = new ArrayList();
list.Add(i); /* boxing */
int j = (int)list[0]; /* unboxing */
使用它的代价是由于拳击和拆箱而缺乏类型安全性和性能。
.NET 2.0方式是使用泛型:
List<int> list = new List<int>();
list.Add(i);
int j = list[0];
装箱的价格(据我所知)是需要在堆上创建一个对象,将堆栈分配的整数复制到新对象,反之亦然,以便拆箱。
仿制药的使用如何克服这个问题?堆栈分配的整数是否保留在堆栈上并从堆中指向(我想这不是这种情况,因为当它超出范围时会发生什么)?看起来仍然需要将其复制到堆栈外的其他地方。
真正发生了什么?
答案 0 :(得分:64)
当涉及到集合时,泛型可以通过在内部利用实际的T[]
数组来避免装箱/取消装箱。例如List<T>
使用T[]
数组来存储其内容。
数组当然是一种引用类型,因此(在当前版本的CLR中,yada yada)存储在堆上。但由于它是T[]
而不是object[]
,因此数组的元素可以“直接”存储:也就是说,它们仍然在堆上,但它们位于堆中数组而不是盒装,并且数组包含对框的引用。
因此,对于List<int>
,您在数组中拥有的内容将“看起来”如下:
[ 1 2 3 ]
将此与使用ArrayList
的{{1}}进行比较,因此会“看起来”像这样:
[ *a *b *c ]
...其中object[]
等是对象的引用(盒装整数):
*a -> 1 *b -> 2 *c -> 3
原谅那些粗略的插图;希望你知道我的意思。
答案 1 :(得分:63)
您的困惑是由于误解了堆栈,堆和变量之间的关系。这是考虑它的正确方法。
作为实现细节,可以在堆栈上分配保证短期存储的存储位置。在堆上分配可能长寿的存储位置。请注意,这并未说明“值类型总是在堆栈上分配”。值类型 not 总是在堆栈上分配:
int[] x = new int[10];
x[1] = 123;
x[1]
是一个存储位置。它是长寿的;它可能比这种方法寿命更长。因此它必须在堆上。它包含int的事实是无关紧要的。
你正确地说出为什么盒装int很贵:
装箱的价格是需要在堆上创建一个对象,将堆栈分配的整数复制到新对象,反之亦然,以便取消装箱。
你出错的地方是说“堆栈分配整数”。分配整数的位置无关紧要。重要的是它的存储包含整数,而不是包含对堆位置的引用。价格是需要创建对象并进行复制;这是唯一相关的成本。
那么为什么通用变量成本不高?如果你有一个T类型的变量,并且T被构造为int,那么你有一个int,period类型的变量。 int类型的变量是存储位置,它包含int。 存储位置是在堆栈上还是堆完全不相关。相关的是存储位置包含一个int ,而不是包含对堆上某些内容的引用。由于存储位置包含int,因此您不必承担装箱和拆箱的成本:在堆上分配新存储并将int复制到新存储。
现在清楚了吗?
答案 2 :(得分:3)
ArrayList只处理类型object
,因此要使用此类需要在object
之间进行转换。在值类型的情况下,此转换涉及装箱和拆箱。
当您使用通用列表时,编译器会输出该值类型的专用代码,以便实际值存储在列表中,而不是对包含值的对象的引用。因此不需要拳击。
拳击的价格(据我所知)是需要在堆上创建一个对象,将堆栈分配的整数复制到新对象,反之亦然,以便取消装箱。
我认为你假设值类型总是在堆栈上实例化。情况并非如此 - 它们可以在堆上,堆栈上或寄存器中创建。有关这方面的更多信息,请参阅Eric Lippert的文章:The Truth About Value Types。
答案 3 :(得分:3)
泛型允许列表的内部数组输入int[]
而不是有效object[]
,这需要装箱。
以下是没有泛型的情况:
Add(1)
。1
被装入一个对象,这需要在堆上构建一个新对象。ArrayList.Add()
。object[]
。此处有三个间接级别:ArrayList
- &gt; object[]
- &gt; object
- &gt; int
。
使用泛型:
Add(1)
。List<int>.Add()
。int[]
。因此只有两个间接级别:List<int>
- &gt; int[]
- &gt; int
。
其他一些差异:
答案 4 :(得分:1)
在.NET 1中,调用Add
方法时:
i
变量的内容将复制到参考在.NET 2中:
i
的副本将传递给Add
方法是的,i
变量仍然被复制(毕竟,它是一个值类型,并且值类型总是被复制 - 即使它们只是方法参数)。但是堆上没有冗余的副本。
答案 5 :(得分:1)
为什么要考虑WHERE
值存储的对象?在C#中,值类型可以存储在堆栈和堆中,具体取决于CLR选择的内容。
在泛型产生影响的地方,WHAT
存储在集合中。在ArrayList
的情况下,集合包含对盒装对象的引用,其中List<int>
包含int值本身。