题
。净1.0的方式创造的收集整数(例如)是:
ArrayList list = new ArrayList();
list.Add(i); /* boxing */
int j = (int)list[0]; /* unboxing */
刑罚的使用,这是缺乏类安全和性能,由于拳击和拆箱.
。网2.0的方法是使用泛型:
List<int> list = new List<int>();
list.Add(i);
int j = list[0];
价格的拳击(以我的理解)是需要建立一个对象堆上复制堆整数分配到新的对象,反之亦然对拆箱.
如何使用仿制药克服这个吗?不堆分配整数停留在堆和被指为从堆(我猜不是这种情况是因为会发生什么时候它会得出的范围)?它似乎是,仍然需要复制它在其他地方出的堆。
什么是真的呢?
解决方案
当涉及到集合,泛型使得能够避免装箱/通过利用内部实际T[]
阵列拆箱。 List<T>
例如使用T[]
数组来存储其内容。
在阵列,当然,是引用类型,因此(在CLR中,内容十分重要的当前版本)存储在堆上。但是,由于它是一个T[]
而不是object[]
,阵列的元件可以“直接地”存储:即,他们仍然在堆上,但他们在堆上的阵列在代替的是盒装和具有该阵列包含在箱子上的引用。
所以对于一个List<int>
,例如,你不得不在阵列中会“看”是这样的:
[ 1 2 3 ]
比较这对一个ArrayList
,它使用一个object[]
,因此将“看”是这样的:
[ *a *b *c ]
...其中*a
等是对对象的引用(盒装整数):
*a -> 1 *b -> 2 *c -> 3
打扰那些粗插图;希望你知道我的意思。
其他提示
你的困惑是一结果的误会是什么之间的关系叠,堆和变量。这里是正确的方式去想它。
- 一个变量是一个存储位置具有一个类型。
- 生命期的一个变量可以是短期或长。通过"简短"我们的意思是"一直到当前的功能返回或引发"和"长期"我们的意思是"可能超过"。
- 如果该类型的变量是一个基准类型,那么内容的变量是一个参考到一个长期的存储位置。如果该类型的变量是一个值的类型,然后变量的内容是一种价值。
作为一个执行情况的细节、存储位置,这是保证是短暂的,可以分配。存储位置,它可能是长期存在的分配上堆。注意到该说任何关于"值类型总是分配上的堆。" 值的类型 不 总是分配上的栈:
int[] x = new int[10];
x[1] = 123;
x[1]
是的存储位置。它是长期;它可能会活得比这种方法。因此,它必须在堆。事实上,它包含一个int是无关紧要的。
你正确地说为什么一盒装int是昂贵:
价格的拳击是需要建立一个对象堆上复制堆整数分配到新的对象,反之亦然对拆箱.
你去哪里错了是说"堆叠分配整数".它不论在哪里整数被分配。什么事情是,它的存储 包含整数, ,而不是包含 引用一堆的位置.价格是需要创建的目的和做的复制;这是唯一费用就是相关的。
那么为什么不是一个通用的可变成本高昂的?如果你有一个可变的种类型的T,和T构成要int,然后你有一个可变的种类型int的时期。一个可变的种类型int是一个存储位置,并且它包含一个int. 是否存储位置是在堆或堆是完全不相干.什么是相关的是,存储位置 包含一个int, ,而不是包含 一个参考的东西堆上.由于存储位置包含一个int,你不需要采取费用的拳击和拆箱:分配新的存储在该堆中和复制的int到新的存储。
是,现在清楚了吗?
仿制药可以让列表的内部阵列类型 int[]
而不是有效的 object[]
, 这将需要拳击。
这里发生了什么没有泛型:
- 你打电话
Add(1)
. - 在整数
1
是盒装成一个对象,这就需要一个新的对象是构成堆上的. - 这个对象是传递给
ArrayList.Add()
. - 装箱的对象是塞进一个
object[]
.
有三个级别的间接在这里: ArrayList
-> object[]
-> object
-> int
.
与泛型:
- 你打电话
Add(1)
. - Int1是通过来
List<int>.Add()
. - Int被塞进一个
int[]
.
所以只有两个级别的间接: List<int>
-> int[]
-> int
.
其他一些差别:
- 非一般的方法将需要一笔8或12个字节(一指针,一个int)储存的价值、第4/8在一项分配和4。这可能会更由于对准和填充。通用的方法将只需要4个字节的空间数。
- 非一般的方法需要分配盒装int;通用方法不可行。这是速度更快和降低GC流失。
- 非一般的方法需要转换取价值观。这不是类型安全,它的一位速度较慢。
一个ArrayList只处理类型object
所以使用此类需要铸造,并从object
。在值类型的情况下,该铸造包括装箱和拆箱。
当您使用的通用列表专门代码为该值类型的编译器输出,以使实际值被存储在列表中,而不是包含该值的对象的引用。因此,没有拳击是必需的。
拳击的价格(我的理解)是需要在堆上创建一个对象,堆栈分配整数复制到新对象,反之亦然为开箱。
我想你是假设值类型总是实例化堆栈。不是这种情况下 - 它们可以在堆上被创建,在栈或寄存器。有关此,请参阅埃里克利珀的文章的详细信息:的真相值类型的。
中。净额1,当 Add
方法被称为:
- 空间上的分配堆;一个新的参照了
- 的内容
i
变量的复制到参考 - 一个复制的基准是把在列表的最后
中。净2:
- 一个复制的可变
i
传递到Add
方法 - 的副本,该副本被放在列表的最后
是的, i
变量仍然是复制(毕竟,这是一个值的类型和价值类型总是复制-即使他们只是方法的参数)。但是没有多余的副本制成堆上的.
你为什么要WHERE
的思维值\对象存储?在C#的值类型可以存储在栈以及堆取决于什么CLR选。
其中泛型有所作为被WHAT
存储藏。在ArrayList
的情况下,收载到作为List<int>
包含自己INT值盒装对象的引用。