Search code examples
c#deep-copycopying

Properly copying data around in C#


I'm working on some simulation code in C#, and I have some code along the lines of:

public void predict(Point start, Point end)
{
    end.velocity = start.velocity + dt * end.acceleration;
    end.position = start.position + dt * end.velocity;
}

Where position, velocity, acceleration are some vector data types I defined with associated operators.

As well as code where I'm doing:

StartPoint = EndPoint;
EndPoint = CurrentPoint;

With the *Points being instances of Points that have several primitive (double) and non-primitive (Vector) data types.

I'm running into the (obvious) issue that the above code, most likely will simply just set StartPoint to point to the data that was previously EndPoint, and EndPoint will point to CurrentPoint.

Meaning, if I modify CurrentPoint again I'll end up accidentally modifying EndPoint.

In C++ this is simple to prevent since I can define my assignment operator to do a deep copy of the underlying data within my Point objects. How can I prevent this in C#?

Thanks for any help!

Edit: The Vector class is defined as

[Serializable]
public class Vector
{
    private Double[] data = new Double[Constants.Dimensions];

    ... snip ...

    public static Vector operator +(Vector lhs, Vector rhs)
    {
        Vector result = new Vector();
        for (UInt32 i = 0; i < Constants.dimensions; i++)
            result[i] = lhs[i] + rhs[i];
        return result;
    }

    lots more code here 
}

Solution

  • This is one of the nastiest problems with the C# design IMHO.

    If 'Point' is a struct (value), then a memberwise copy will be made, so x = y will make an independent copy of y. But if it is a class (reference), x = y will simply point the reference x to the same storage used for y, so the two will simply become different 'aliases' for the same data.

    The two solutions I know of for your issue are:

    • Use a struct. This will give you the value-type behaviour that you expect for maths classes. To keep your code efficient you may then need to pass by reference everywhere to avoid the structs being copied continually.

    • Use a class, but be very very careful when using = to make sure you retain an independent copy of the data. You'll need to change x = y to something else, e.g. x = new Point(y);.