C#: List of struct

Talking about discoveries, I discovered something about C# today.

Let’s say I have a struct ‘MyStruct’ with an integer as its data member. and I create a list of this type.

List list = new List();

I add a few elements to this list.

list.Add(new MyStruct(10));
list.Add(new MyStruct(20));

If I try to modify any element in this list, that is when things become interesting.

list[0].m_age = 30;

The compiler starts its blaring sirens. Error: Cannot modify the return value of System.Collections.Generic.List<MyStruct>.this[int]‘ because it is not a variable

This problem does not arise if you have a list of class instead of a structure. i.e, if I change MyStruct to a class (and call it MyClass to avoid confusion), this error message goes away. Why should it matter for structs and not for classes? I knew the basic difference between these two, one being a value type and the other being a reference type. This knowledge wasn’t enough to crack this puzzle.

A quick googling gave me the answer. The operator [] is actually a function call. When you index a list and get the individual element, you are actually making a function call. You get the individual element as the return value of the function. This return value is placed on the stack and when you try to use the dot operator on this, you are actually trying to modify the return value of the function. i.e. you are modifying a copy of the element and your modifications will not affect the element in the list. The compiler, smart it is, catches this and informs you of the pitfall.

This does not affect a list of class items because class is a reference type. When you use [] operator on a list of class, the [] function is actually returning a reference to the original object and hence your modification on this reference object will cause the original element to change.

Phew! One discovery that was. Next time you are creating a list of structs, remember this little piece of information.

Update:

Beware! If you save the return value of the function in a variable, modify that variable and expect the actual list element to change, that’s not going to happen.

MyStruct s = list[0];
s.m_age = 40; // This will not change the element list[0]

Modifying the variable s is not going to modify list[0].

This poses an interesting question. Once you create a list of structs and initialize it, there is no way you can modify it? I need to find this out. Look out for more updates on this.

About these ads

30 thoughts on “C#: List of struct

  1. Stéphane says:

    Hello there,
    I just found out the same sh*t…
    So, from what i understand, if you have a List of a type in your program, you should rather make your type a class than a struct.
    First of all you’ll be able to access the properties of your objects and modify them directly in the List, plus, it will take less memory, since you’ll have a list of reference to your objects, and not a List of structs.
    Am I right?

    [quote]I need to find this out. Look out for more updates on this.[/quote]

    So do you have some news :-)

    Stephane

  2. generally says:

    Hello Stephen,

    >So, from what i understand, if you have a List of a type in your program, you >should rather make your type a class than a struct.

    Exactly.

    >First of all you’ll be able to access the properties of your objects and modify them >directly in the List

    Right again.

    >… plus, it will take less memory, since you’ll have a list of reference to your >objects, and not a List of structs.

    Not really. If you have a list of structs, the list is created on the stack, whereas a list of class objects will be created on the heap.

    >[quote]I need to find this out. Look out for more updates on this.[/quote]
    >So do you have some news :-)

    Nothing new. I got a clear indication that Microsoft wants developers to stay away from mutable structs. So, stay away :)

  3. Ogom Okafor says:

    I’m sure this goes without saying but if you don’t want to change all your structs to classes, you always have the option of re-assigning the modified copy to the struct.
    MyStruct s = list[0];
    s.m_age = 40; // This will not change the element list[0]
    list[0] = s;

  4. generally says:

    Yes, Ogom, you are absolutely right. You can use this solution in some cases, but if your struct is quite big, then the whole struct needs to be copied and then re-assigned. You are better off with a class, in that case.

  5. Shingo-Y says:

    I think the answer is simple.
    You might know that a structure accepts function definition.
    So your structure should be:
    struct MyStruct
    {
    private int m_nAge;
    public void SetAge(int newAge) { m_nAge = newAge; }
    };

    Then you can call…
    list[0].SetAge(30)
    …and it should work ^^

  6. generally says:

    You are right, Shingo. It will not work because you are calling the SetAge method on a copy of the original struct object. The whole problem lies in the difference in the assignment (objectA = objectB;) for references and value types.

  7. merc says:

    so… does this boil down to:
    a) If you want a “read only” kind of list item, use struct
    b) If you want a “later to be modify” kind of list item , use class

    I don’t understands, why is it implement this way? Not to mention this is going to break my coding behavior, which I normally use struct for “data container” while class for something that contain operation that are more then just a data storage unit.

  8. anaamica says:

    Yes merc, you got it right.

    As to why it is implemented this way, only Microsoft can answer that.

  9. Jared says:

    It’s just a limit of C#. As mentioned earlier, the [] accessors for the list are just functions, you might as well be calling something like GetItemAtIndex(0). In C#, functions cannot return references to value types, they can only return value types and reference types. Structs are always value types, so returning a struct from a function can always only be a copy.

  10. anaamica says:

    Thanks for the answer, Jared.

  11. jheidt says:

    This is the pure definition of a value type, value types are put on the stack, copying in it’s entirety all value types in its implementation.
    If this behavior seems to be a problem, I strongly recommend you rethink why you are using a value type. If you need references to be maintained and dont want to blow your stack size to kingdom come, I recommend you use reference types (classes). If you are concerned about ‘copying’ the values, realize that every method call [] you make is copying the entire struct to the stack frame where you are.
    If the value types are large, and you go deep into the stack, it’s quite a bit of memory movement. If these were reference types you wouldn’t have that behavior, and, in theory, perf would benefit from the lack of memory movement.

    My 0.02.

    .jfh

  12. Pxtl says:

    There are pros and cons to the “list of structs”.

    First, the pros: there is no boxing/unboxing penalty here. So, you’re working with naked structs. Sure, they’re effectively read-only in that collection, but they’re stored in a manner that means no mucking about with references, garbage-collection, etc.

    The cons are that you can’t alter their fields when inside the list, and that you’re using structs. Structs are the bastard-child of C#, so using them is always a “con”.

    Now, the thing to remember is that everything is by-value passing. You can’t edit an object in your list, but you _can_ replace it.

    So, to edit a field, it’s

    MyStruct s = myList[index];
    s.MyField = “Foo”;
    myList[index] = s;

    see? You copy it out of the list, edit it, and return it.

    In general, I have this point of advice with structs: no functions/properties should edit data. Any setting of struct fields should happen in the constructor or as public fields. Structs have all kinds of gotchas, but if you follow that guideline the compiler will protect you from them.

    Structs are important, since their copying-semantics means it’s impossible to run into the aliasing/copying problems you run into with classes. That is, if you’re passing a lot of references to a few classes around, and you change it on X and find yourself accidentally changing it on Y and Z when you didn’t want it to, then you should consider a struct. Alternately, you could clone the object a lot, but cloning is a type-safety nightmare.

    Still, if you use ICollection as a wrapper around your list (ICollection is read-only) then you get a nice easy way to do read-only data, since the structs are read-only in a collection, and ICollection can only be sized and iterated-across. Unfortunately, ICollection doesn’t have a “Get” indexer, which is a sad omission.

  13. Waqas says:

    can anyone tell me how to read the list.

  14. Stephen Ozoigbo says:

    MyStruct s = myList[index];
    s.MyField = “Foo”;
    myList[index] = s;

  15. Mattie Shoes says:

    Wow, I was just looking for how to return a reference to a value in C#… I’m going to have to restructure a few classes to account for this inability. :-(

    Shoulda written it in C++ in the first place, but, c’est la vie…

  16. Some guy says:

    For all those who complain about the inability to change structs. And for those who blame ms implementation for it:
    You have some catching up todo on your basic programming skills. If you’re unwilling or unable to understand the difference:
    Please get a new job. Or at least manage that I never have to deal with your code.
    Sry for this guys but there we some people explaining the difference and the why pretty precisely and others still complain why it is not in c# to modify structs.
    I cant even imagine them prgramming in a language where they are not as protected (C++ was mentioned). This scares the shit out of me!

  17. Some guy's friend says:

    I agree with Some guy.

    The whole purpose of Struct is for performance optimization.
    Use it if it’s readOnly.
    Else, use Class.
    Simple.
    Also, i think there’s already efforts explaining why a struct is not writable.

  18. Divad4686 says:

    What if you need 10.000.000 of objects and you need to modifi then? you can only use structs here because that 8 Byte of header for every object its going to hurt your memory really bad.

  19. tayron says:

    agree with Divad4686. structs use up less memory than classes. you should be able to modify them directly, like in C++. classes aren’t feasible when you have a huge list.

    • Bob Holness says:

      Then use an array of them instead.

      var array = SomeStruct[100];
      array[0].SomeField = 3; // this modifies the element without the problems mentioned

  20. T_T says:

    and here I was this whole time trying to figure out why it wouldn’t work.

  21. Stephen says:

    MyStruct s = myList[index];
    s.MyField = “Foo”;
    myList[index] = s;

    I realize this discussion is quite old early on, but I would really like to know why the code above works. If the list indexer is just a function that returns a copy of the struct, how can you do myList[index] = s? Aren’t you just assigning s to a temporary copy? Isn’t myList[index] on the left-hand side of the assignment operator just returning a temporary copy of the struct at that index? Or is myList[index] purely a reference when it’s to the left of =?

    • Some Other Guy says:

      Stephan,

      I have not validated this, but, in C# you have “Properties” which are members of classes that also implement set and/or get methods.

      Properties are accessed like members, but they activate the corresponding get / set function when assigned or called. for example :

      instead of having :
      private int myInt;

      public setTheInt(int i_int)
      {
      this.myInt = i_int;
      }

      you would have :

      int myInt
      {
      set
      {
      this.myInt = Value;
      }
      get
      {
      return myInt;
      }
      }

      Then your would use it like this :
      object.myInt = 5;
      intTempInt = object.myInt;

      Used like a member, but actually runs like a member + methods to deal with.

      So, to our issue, myList[index], if used with “=” operator, will trigger the set function, and if used on right hand side, will trigger the get function.

  22. Stephen says:

    Thanks, Some. I checked the IL code of an assembly of mine (created in C#) that works with a List of Structs. What I found confirms what you said:

    MyStruct s = myList[index];
    myList[index] = s;

    The first statement in IL is a call to a get_Item method. The second statement is a call to a set_Item method.

  23. andrew says:

    Thanks! I was struggling with this just now, and you really clarified it for me.

  24. king*six says:

    Thanks for the great explanation! I have an addendum, however. The contents of a list of structs can be modified indirectly (MyStruct s = myList[index]; s.item = 12; myList[index].item == 12), but only under the condition that the list is static.

  25. Asad Ali Malik says:

    what i used to do is that make a new
    mystruct item and save the values in it. then list.removeat(index) and then list.insertat(index,mystruct).

  26. Fabian Viking says:

    This is a trick I use to keep the code cleaner:
    //inside the struct
    pu

    list[0].SetAge(30)

  27. Fabian Viking says:

    This is a trick I use to keep the code cleaner:
    //inside the struct
    public MyStruct SetAge(int age)
    {
    myAge = age;
    return this;
    }
    //now you can make these one line changes
    list[0] = list[0].SetAge(30)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: