4

In the following code, a struct is obtained from an array and from a list. When getting the item by index, the array appears to do it by reference whereas the list appears to do it by value. Can someone explain the reasoning behind this?

struct FloatPoint {
    public FloatPoint (float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    public float x, y, z;
}

class Test {

    public static int Main (string[] args) {
        FloatPoint[] points1 = { new FloatPoint(1, 2, 3) };

        var points2 = new System.Collections.Generic.List<FloatPoint>();
        points2.Add(new FloatPoint(1, 2, 3));

        points1[0].x = 0; // not an error
        points2[0].x = 0; // compile error

        return 0;
    }
}

Changing the struct definition to a class makes both compile.

Thomas Eding
  • 35,312
  • 13
  • 75
  • 106
  • 2
    Structs are value types. Lists overload the `[]` operator to return an object of type T. Arrays are special in that they're built-in and give direct access to their elements :-) – Cameron Dec 05 '12 at 20:52
  • What is the error, and why do you say this `array appears to do it by reference whereas the list appears to do it by value`? – Mike Perrenoud Dec 05 '12 at 20:52
  • 1
    @BigM the exact error text is: `Cannot modify the return value of 'System.Collections.Generic.List.this[int]' because it is not a variable`. As for his quote, it's actually more or less correct. The list indexer returns a copy, the array access doesn't. – Servy Dec 05 '12 at 20:56

1 Answers1

6

When you get a struct, it is always by value. The structure will be copied, you don't get a reference to it.

The difference is that you can access the sctruct directly in the array, but not in the list. When you change the property in the struct in the array, you access the property directly, but to do the same with a list you have to get the struct, set the property, then store the struct back in the list:

FloatPoint f = points2[0];
f.x = 0;
points2[0] = f;

Earlier versions of the compiler would let you write the code that you have, but for a list it would generate code similar to this:

FloatPoint f = points2[0];
f.x = 0;

I.e. it would read the struct, change it, and silently throw the changed struct away. The compiler was changed to give an error in that case.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • For reasons like this, [mutable structs are considered evil by many](http://stackoverflow.com/questions/441309/). Therefore it is strongly recommended that you make your struct _immutable_. You can do that with `readonly`: `public readonly float x, y, z;`. Now, you _have_ to swap the entire struct object with a new one each time you want to make changes. And that would be the same with arrays and lists: `point[0] = new FloatPoint(0, point[0].y, point[0].z);` – Jeppe Stig Nielsen Dec 17 '12 at 15:28