Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get struct item by index in list and array

Tags:

arrays

c#

When I use an array of structs (for example System.Drawing.Point), I can get item by index and change it.

For example (This code work fine):

Point[] points = new Point[] { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Length; i++)
{
    points[i].X += 1;
}

but when i use List it's not work:

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

Example(This code didn't work fine):

List<Point> points = new List<Point>  { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++)
{
    points[i].X += 1;
}

I know that when I get list item by index I got copy of it and compiler hints that I did not commit an error, but why take the elements of the array index works differently?

like image 830
bogza.anton Avatar asked Dec 04 '15 15:12

bogza.anton


2 Answers

This is because for the array points[i] shows where the object is located. In other words, basically points[i] for an array is a pointer in memory. You thus perform operations on-the-record in memory, not on some copy.

This is not the case for List<T>: it uses an array internally, but communicates through methods resulting in the fact that these methods will copy the values out of the internal array, and evidently modifying these copies does not make much sense: you immediately forget about them since you do not write the copy back to the internal array.

A way to solve this problem is, as the compiler suggests:

(3,11): error CS1612: Cannot modify a value type return value of `System.Collections.Generic.List<Point>.this[int]'. Consider storing the value in a temporary variable

So you could use the following "trick":

List<Point> points = new List<Point>  { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++) {
    Point p = points[i];
    p.X += 1;
    points[i] = p;
}

So you read the copy into a temporary variable, modify the copy, and write it back to the List<T>.

like image 92
Willem Van Onsem Avatar answered Nov 20 '22 02:11

Willem Van Onsem


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.

Other words, when using the List<T>, you're are creating copies.

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.

Solution:

List<Point> points = new List<Point>  { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++)
{
    Point p = points[i];
    p.X += 1;
    //and don't forget update the old value, because we're using copy
    points[i] = p;
}
like image 1
isxaker Avatar answered Nov 20 '22 03:11

isxaker