3

I am a bit confused with a concept of value types as properties in a class. As far as I understand, when getter is called - a copy of value type is returned. Let's have an example:

public struct Point
{
    public int X;
    public int Y;

    public void Set(int x, int y)
    {
        X = x;
        Y = y;
    }
}

public class Test
{
    public Int32 X { get; set; }

    public void SetX(int x) => X = x;

    public Point Point { get; set; }
}

static void Main(string[] args)
{
    var test = new Test();
    test.SetX(10);
    test.Point.Set(20, 30);

    Console.WriteLine(test.X);
    Console.WriteLine($"{test.Point.X} {test.Point.Y}");
}

Point is not modified, since it is a value type, and getter gives us a copy, which makes sense. The thing I don't understand - why in this case X is modified? It is a value type also. This looks like a contradiction to me. Please help me to understand what I am missing

Julian
  • 5,290
  • 1
  • 17
  • 40
jon bee
  • 83
  • 3
  • 1
    In the Point case, you're making a change *to the value returned*. In the SetX case you're making a change *to the object containing X*. They're very different scenarios. If you had a `Test.SetPoint` method (e.g. `public void SetPoint(Point point) => Point = point;` then you'll see that method change the value too... – Jon Skeet Jul 27 '23 at 15:16
  • @Jon Skeet, but X in the object is a property with type int, doesn't that mean that when we type "object.X" - it returns a copy of X? I read your articles on value and ref types, and I understand it in a case of passing them as parameters to methods, though I am still confused when value type is a part of ref type, and it is used as a property – jon bee Jul 27 '23 at 15:22
  • Yes, it returns a copy of the current value of X. But `SetX` changes that to a different value, just as if you set `test.X = 50;`. – Jon Skeet Jul 27 '23 at 15:28
  • I guess this is the reason you shouldn't use mutable structs. – Charlieface Jul 27 '23 at 15:51

2 Answers2

2

Point is not modified, since it is a value type, and getter gives us a copy, which makes sense

Yes, because you get a make changes to local copy of Point returned by auto-property getter.

The thing I don't understand - why in this case X is modified?

Because you are modifying object containing X not the copy of X. X = x invokes setter (there is no "returns copy" happening here) for auto-property which will write corresponding value to corresponding memory location.

Code for Point simulating this behavior can look for example like:

public void SetPoint(int x, int y) => Point = new Point{X= x, Y =y};

And in both cases the following will work:

test.X = 42;
test.Point = new Point {X = 7, Y = 42}

Demo @sharplab.io

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • to clarify: "test.Point.SomeMethod" - creates a copy of Point, whereas "test.Point = ..." - does not create, right? – jon bee Jul 27 '23 at 15:39
  • 2
    In the first case you are reading the Point value (=make a copy), to access that SomeMethod. In the second case you are assigning a new value to the Point property of test – Hans Kesting Jul 27 '23 at 15:52
  • 3
    Arguably mutable structs are a bad idea anyway – Charlieface Jul 27 '23 at 15:52
  • @Hans Kesting, thank you, your comment seems "switched" me – jon bee Jul 27 '23 at 15:54
  • 2
    @jonbee _"to clarify: `test.Point.SomeMethod` - creates a copy of `Point`"_ - yes, because in this case `test.Point` represents read access (property getter), so this results in copying and then you invoke a method on the read data(copy), while in `test.Point = new Point...` there is write happening to the `Point` property. – Guru Stron Jul 27 '23 at 16:10
  • 1
    Also see [Why are mutable structs “evil”?](https://stackoverflow.com/questions/441309/why-are-mutable-structs-evil). As Charlieface correctly pointed - mutable structs are quite often are questionable choice. – Guru Stron Jul 27 '23 at 16:11
0

To modify 'Test.Point' code this:

        static void Main(string[] args)
        {
            var test = new Test();
            test.SetX(10); // you assign a new value to the property
            // test.Point.Set(20, 30); - you change a copy of the value without assignment of the result to the property  
            test.Point = new Point(){ X = 20, Y = 30 }

            Console.WriteLine(test.X);
            Console.WriteLine($"{test.Point.X} {test.Point.Y}");
        }

Among ValueTypes the struct only can have (user defined) properties or methods which can modify itself. It confuses a lot. You can follow recommendation to use the read-only structs to avoid any possibility of such behavior which is hard to identify.

rotabor
  • 561
  • 2
  • 10