(This is not a duplicate of What is the difference between a field and a property in C# - but a more subtle issue)
I have an immutable class Camera (from this Ray Tracing example) which works fine:
public class Camera
{
public Vector3D Pos;
public Vector3D Forward;
public Vector3D Up;
public Vector3D Right;
public Camera(Vector3D pos, Vector3D lookAt)
{
Pos = pos;
Forward = lookAt - pos;
Forward.Normalize();
Vector3D Down = new Vector3D(0, -1, 0);
Right = Vector3D.CrossProduct(Forward, Down);
Right.Normalize();
Right *= 1.5;
Up = Vector3D.CrossProduct(Forward, Right);
Up.Normalize();
Up *= 1.5;
}
}
As this is an immutable class, I wanted to change the fields to read-only properties, which I have done throughout the application with no problem, thus:
public Vector3D Pos { get; private set; }
public Vector3D Forward { get; private set; }
public Vector3D Up { get; private set; }
public Vector3D Right { get; private set; }
the rest of the code is unchanged - but the application no longer works correctly. Upon investigation, the values of Forward, Up, and Right are no longer normalized. Normalize, called 3 times in the constructor (above) is a void method. It appears that calling such a method on the property rather than on a field (at least, in the constructor) does not cause the property's value to be updated. It almost appears as though the vector (a struct) is being passed by reference rather than by value, if that makes any sense.
The only way I could find to get this to work was to do all my transforms in the constructor before setting the property values, as shown below:
public Camera(Vector3D pos, Vector3D lookAt)
{
var forward = lookAt - pos;
forward.Normalize();
var down = new Vector3D(0, -1, 0);
var right = Vector3D.CrossProduct(forward, down);
right.Normalize();
right *= 1.5;
var up = Vector3D.CrossProduct(forward, right);
up.Normalize();
up *= 1.5;
Pos = pos;
Forward = forward;
Up = up;
Right = right;
}
Can anyone explain to me what exactly is going on here? I understand that there is a difference between a property and a field if you are adding any additional behaviour in the get or set, but I'm not.