Roman Starkov
Roman Starkov

Reputation: 61510

In C#, are the values in a List<struct> boxed?

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?

Upvotes: 36

Views: 11397

Answers (7)

John Leidegren
John Leidegren

Reputation: 61057

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 treat 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 it 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 can't do reflection with Java generics while you can with the .NET generic type system.

Upvotes: 4

ajma
ajma

Reputation: 12206

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

Upvotes: 40

Marc Gravell
Marc Gravell

Reputation: 1064104

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;}
}

Upvotes: 22

ScottS
ScottS

Reputation: 8553

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

Upvotes: 2

Christian C. Salvad&#243;
Christian C. Salvad&#243;

Reputation: 827992

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.");

Upvotes: 0

Anton Gogolev
Anton Gogolev

Reputation: 115867

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;

Upvotes: 12

arul
arul

Reputation: 14084

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

Upvotes: 1

Related Questions