Pr.Dumbledor
Pr.Dumbledor

Reputation: 656

struct in C# doesn't work as expected

I'm working on a simple application and I'm a little confused. I have a simple struct Point with int x and int y. And I use it for Line

public class Line : Shape {

    public Line() {
        PointA = new Point(x: 0, y: 0);
        PointB = new Point(x: 0, y: 0);
    }

    public Point PointA { get; set; }
    public Point PointB { get; set; }
}

and somewhere

var line = new Line();
line.PointB = new Point(x: 4, y: 2);
Console.WriteLine($"Line start at {line.PointA.GetX()}:{line.PointA.GetY()}; end at {line.PointB.GetX()}:{line.PointB.GetY()}");

for (int i = 0; i < 10; i++) {
    line.PointB.IncrementX();
    line.PointB.IncrementY();
}
Console.WriteLine($"Line start at {line.PointA.GetX()}:{line.PointA.GetY()}; end at {line.PointB.GetX()}:{line.PointB.GetY()}");

Here need to increment x and y of Point but result doesn't change:

Line start at 0:0; end at 4:2
Line start at 0:0; end at 4:2

What I'm doing wrong? It seems strange. Are there some specific rules to use struct in C#. I know that this is a value type but I think it is a good for Point. All examples uses struct for Point. Please help?

Point:

public struct Point {

    private int _x;
    private int _y;

    public Point(int x, int y)
    : this() {
        _x = x;
        _y = y;
    }

    public void IncrementX() {
        _x++;
    }
    public void IncrementY() {
        _y++;
    }

    public int GetX() {
        return _x;
    }
    public int GetY() {
        return _y;
    }
}

Upvotes: 2

Views: 186

Answers (1)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236318

Struct is a value type. And it is passed by value (i.e. by creating copy of all fields) instead of passing reference to struct instance. So when you do

line.PointB.IncrementX()

When you call getter of PropertyB, it returns copy of Point which is stored at PropertyB backing field. And then you call increment on copy. Thus original value will stay unchanged.

Further reading: Value and Reference Types and especially Mutating Readonly Structs which says

mutable value types are evil. Try to always make value types immutable.


What you can do if you want to actually move line point?

  • Change Point type to class. Then it will be passed by reference, and all methods will be called on original point which you store in Line.

  • Assign new (modified) point instance to line

I.e. you should store copy, change it and assign back

var point = line.PointB; // get copy
point.IncrementX();      // mutate copy
point.IncrementY();
line.PointB = point;     // assign copy of copy

You can also make your Point struct immutable (the best thing you can do for value types):

public struct Point
{
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; }
    public int Y { get; }

    public Point IncrementX() => new Point(X + 1, Y);
    public Point IncrementY() => new Point(X, Y + 1);
    public Point Move(int dx, int dy) => new Point(X + dx, Y + dy);
}

And now changing location will look like

line.PointB = line.PointB.Move(1, 1);

Upvotes: 6

Related Questions