tags:

views:

596

answers:

7

Suppose I declare a generic List containing values of a struct type:

struct MyStruct {
    public MyStruct(int val1, decimal val2) : this() {
        Val1 = val1;
        Val2 = val2;
    }
    public int Val1 {get; private set;}
    public decimal Val2 {get; private set;}
}

List<MyStruct> list;

Does the List<> store each individual value as a boxed struct, allocated individually on the heap? Or is it smarter than that?

+1  A: 

There's no boxing, that's why it's a little faster than the non-generic way.

arul
+7  A: 

No, List<T> does not box anything. Internally it stores its values in a simple array:

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection,    IEnumerable
{
    // Fields
    private const int _defaultCapacity = 4;
    private static T[] _emptyArray;
    private T[] _items;
    private int _size;
Anton Gogolev
+15  A: 

There is no boxing.

"No, there will be no boxing. That was one of the main design goals of Generics."

http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/359cf58a-f53d-478e-bc31-1507e77c9454/

"If a value type is used for type T, the compiler generates an implementation of the List<T> class specifically for that value type. That means a list element of a List<T> object does not have to be boxed before the element can be used, and after about 500 list elements are created the memory saved not boxing list elements is greater than the memory used to generate the class implementation."

http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx

ajma
A: 

No boxing, that's one of the Benefits of Generics:

List<int> list1 = new List<int>();    
// No boxing, no casting:
list1.Add(3);

// Compile-time error:
// list1.Add("It is raining in Redmond.");
CMS
A: 

List stores everything in arrays of type T. There is no boxing, but the array is allocated on the heap.

ScottS
+9  A: 

It isn't boxed, but the data will be a copy of the original, and every time you get the data out, it will copy again. This tends to make it easy to lose changes. As such, you should aim to not write mutable structs. Unless MyStruct actually represents a unit-of-measure (or similar), make it a class. And if it is a measure, make it immutable (no public settable members).

And either way, don't expose fields! Use properties ;-p

For example:

struct MyStruct {
    public MyStruct(int val1, decimal val2) {
        this.val1 = val1;
        this.val2 = val2;
    }
    private readonly int val1;
    private readonly decimal val2;
    public int Val1 {get {return val1;}}
    public decimal Val2 {get {return val2;}}
}

Or alternatively (C# 3.0):

struct MyStruct {
    public MyStruct(int val1, decimal val2) : this() {
        Val1 = val1;
        Val2 = val2;
    }
    public int Val1 {get; private set;}
    public decimal Val2 {get; private set;}
}
Marc Gravell
Yep, I appreciate that. Seeing as several people pointed this out, I'll use your struct in the original question, to put the emphasis back onto the boxing and off the value semantics of structs.
romkyns
+2  A: 
typeof(List<>) // or informally List`1

...is a generic type. Generic types or parameterized types are special in that they are compiled in the normal way but at run-time they are compiled yet again depending on their use.

If you put a ValueType in a generic list e.g. List<int> the run-time behavior is to threat that as if it was storing the type int (which is copy-by-value). Boxing doesn't need to take place because you don't need to treat is as if the type of this List`1 is something other than int.

Before .NET 2.0 this could not be done so the infamous ArrayList class maintained an array of objects which meant ValueType (ints or structs) had to be boxed to be placed in that container.

This is not to be confused with Java generics which is something entirely different. Java generics was implemented with type erasure which does not give you that same performance boost. What Java does is that once the compiler has finished type checking, it will cast everything to object. This is why you cant do reflection with Java generics while you can with the .NET generic type system.

John Leidegren