Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing and changing structs as property vs as field

Tags:

c#

struct

Ok, I'll start my question saying that I understand the evil behind mutable structs, but I'm working with SFML.net and using a lot of Vector2f and such structs.

What I don't get it is why I can have, and change the values of, a field in a class and can't do the same with a property, in the very same class.

Take a look at this code:

using System;

namespace Test
{
    public struct TestStruct
    {
        public string Value;
    }

    class Program
    {
        TestStruct structA;
        TestStruct structB { get; set; }

        static void Main(string[] args)
        {
            Program program = new Program();

            // This Works
            program.structA.Value = "Test A";

            // This fails with the following error:
            // Cannot modify the return value of 'Test.Program.structB'
            // because it is not a variable
            //program.structB.Value = "Test B"; 

            TestStruct copy = program.structB;
            copy.Value = "Test B";

            Console.WriteLine(program.structA.Value); // "Test A"
            Console.WriteLine(program.structB.Value); // Empty, as expected
        }
    }
}

note: I'll build my own classes to cover the same functionality and keep with my mutability, but I can't see a technical reason why I can do one and can't do other.

like image 425
NemoStein Avatar asked Dec 03 '22 22:12

NemoStein


1 Answers

When you access a field, you are accessing the actual struct. When you access it through property, you call a method that returns whatever is stored in the property. In the case of a struct, which is a value type, you will get back a copy of the struct. Apparently that copy is not a variable and cannot be changed.

Section "1.7 Structs" of the C# language specification 5.0 says:

With classes, it is possible for two variables to reference the same object and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other.

That explains that you will receive a copy of the struct and not be able to modify the original struct. However, it doesn't describe why it isn't allowed.

Section "11.3.3" of the specifcation:

When a property or indexer of a struct is the target of an assignment, the instance expression associated with the property or indexer access must be classified as a variable. If the instance expression is classified as a value, a compile-time error occurs. This is described in further detail in §7.17.1.

So the returned "thing" from the get accessor is a value and not a variable. That explains the wording in the error message.

The specification also contains an example in section 7.17.1 that is nearly identical to your code:

Given the declarations:

struct Point
{
    int x, y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public int X {
        get { return x; }
        set { x = value; }
    }
    public int Y {
        get { return y; }
        set { y = value; }
    }
}
struct Rectangle
{
    Point a, b;
    public Rectangle(Point a, Point b) {
        this.a = a;
        this.b = b;
    }
    public Point A {
        get { return a; }
        set { a = value; }
    }
    public Point B {
        get { return b; }
        set { b = value; }
    }
}

in the example

Point p = new Point();
p.X = 100;
p.Y = 100;
Rectangle r = new Rectangle();
r.A = new Point(10, 10);
r.B = p;

the assignments to p.X, p.Y, r.A, and r.B are permitted because p and r are variables. However, in the example

Rectangle r = new Rectangle();
r.A.X = 10;
r.A.Y = 10;
r.B.X = 100;
r.B.Y = 100;

the assignments are all invalid, since r.A and r.B are not variables.

like image 52
Anders Abel Avatar answered Dec 05 '22 12:12

Anders Abel