The .NET 1.0 way of creating collection of integers (for example) was:
ArrayList list = new ArrayList();
list.Add(i); /* boxing */
int j = (int)list[0]; /* unboxing */
The penalty of using this is the lack of type safety and performance due to boxing and unboxing.
The .NET 2.0 way is to use generics:
List<int> list = new List<int>();
list.Add(i);
int j = list[0];
The price of boxing (to my understanding) is the need to create an object on the heap, copy the stack allocated integer to the new object and vice-versa for unboxing.
How does the use of generics overcome this? Does the stack-allocated integer stays on the stack and being pointed to from the heap (I guess this is not the case because of what will happen when it will get out of scope)? It seems like there is still a need of copying it somewhere else out of the stack.
What is really going on?
When it comes to collections, generics make it possible to avoid boxing/unboxing by utilizing actual T[]
arrays internally. List<T>
for example uses a T[]
array to store its contents.
The array, of course, is a reference type and is therefore (in the current version of the CLR, yada yada) stored on the heap. But since it's a T[]
and not an object[]
, the array's elements can be stored "directly": that is, they're still on the heap, but they're on the heap in the array instead of being boxed and having the array contain references to the boxes.
So for a List<int>
, for example, what you'd have in the array would "look" like this:
[ 1 2 3 ]
Compare this to an ArrayList
, which uses an object[]
and would therefore "look" something like this:
[ *a *b *c ]
...where *a
, etc. are references to objects (boxed integers):
*a -> 1 *b -> 2 *c -> 3
Excuse those crude illustrations; hopefully you know what I mean.