Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Directly modifying List<T> elements

Tags:

c#

.net

I have this struct:

struct Map {     public int Size;      public Map ( int size )     {         this.Size = size;     }      public override string ToString ( )     {         return String.Format ( "Size: {0}", this.Size );     } } 

When using array, it works:

Map [ ] arr = new Map [ 4 ] {     new Map(10),     new Map(20),     new Map(30),     new Map(40)};  arr [ 2 ].Size = 0; 

But when using List, it doesn't compile:

List<Map> list = new List<Map> ( ) {     new Map(10),     new Map(20),     new Map(30),     new Map(40)};  list [ 2 ].Size = 0; 

Why?

like image 571
Joan Venge Avatar asked Jan 06 '09 00:01

Joan Venge


2 Answers

The C# compiler will give you the following error:

Cannot modify the return value of 'System.Collections.Generic.List.this[int]' because it is not a variable

The reason is that structs are value types so when you access a list element you will in fact access an intermediate copy of the element which has been returned by the indexer of the list.

From MSDN:

Error Message

Cannot modify the return value of 'expression' because it is not a variable

An attempt was made to modify a value type that was the result of an intermediate expression. Because the value is not persisted, the value will be unchanged.

To resolve this error, store the result of the expression in an intermediate value, or use a reference type for the intermediate expression.

Solutions:

  1. Use an array. This gives you direct access to the elements (you are not accessing a copy)
  2. When you make Map a class you can still use a List to store your element. You will then get a reference to a Map object instead of an intermediate copy and you will be able to modify the object.
  3. If you cannot change Map from struct to a class you must save the list item in a temporary variable:

 

List<Map> list = new List<Map>() {      new Map(10),      new Map(20),      new Map(30),      new Map(40) };  Map map = list[2]; map.Size = 42; list[2] = map; 
like image 158
Dirk Vollmar Avatar answered Sep 29 '22 14:09

Dirk Vollmar


Because it is a struct, when using the List<T>, you're are creating copies.

When using a struct, it is better to make them immutable. This will avoids effects like this.

When using an array, you have direct access to the memory structures. Using the List<T>.get_Item you work on a return value, that is, a copy of the structure.

If it was a class, you would get a copy of a pointer to the class, but you would not notice this, since pointers are hidden in C#.

Also using the List<T>.ToArray does not solve it, because it will create a copy of the internal array, and return this.

And this is not the only place where you will get effects like this when using a struct.

The solution provided by Divo is a very good workaround. But you have to remember to work this way, not only when using the List<T> but everywhere you want to change a field inside the struct.

like image 28
GvS Avatar answered Sep 29 '22 16:09

GvS