在 C# 中正确复制数据
我正在用 C# 编写一些模拟代码,并且有一些代码如下:
public void predict(Point start, Point end)
{
end.velocity = start.velocity + dt * end.acceleration;
end.position = start.position + dt * end.velocity;
}
其中位置、速度、加速度是我使用关联运算符定义的一些矢量数据类型。
以及我正在做的代码:
StartPoint = EndPoint;
EndPoint = CurrentPoint;
*Points 是具有多种原始(双精度)和非原始(向量)数据类型的 Points 的实例。
我遇到了一个(明显的)问题,即上面的代码很可能只是将 StartPoint 设置为指向以前的 EndPoint 数据,而 EndPoint 将指向 CurrentPoint。
这意味着,如果我再次修改 CurrentPoint,我最终会意外修改 EndPoint。
在 C++ 中,这种情况很容易预防,因为我可以定义赋值运算符来对 Point 对象中的基础数据进行深层复制。在 C# 中如何防止这种情况发生?
感谢您的帮助!
编辑:Vector 类定义为
[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
}
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
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
恕我直言,这是 C# 设计中最棘手的问题之一。
如果“Point”是一个结构体(值),则将创建一个成员副本,因此 x = y 将会创建 y 的一个独立副本。但如果它是一个类(引用),
x = y
将简单地将引用 x 指向用于 y 的相同存储,因此两者将简单地成为相同数据的不同“别名”。我知道您的问题的两个解决方案是:
使用结构。这将为您提供数学课程所期望的值类型行为。为了保持代码高效,您可能需要在各处通过引用传递,以避免结构被不断复制。
使用类,但在使用 = 时要非常小心,以确保保留数据的独立副本。您需要将
x = y
更改为其他内容,例如x = new Point(y);
。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);
.您可以使用 Clone(),然后按照您需要的方式实现深复制吗?
Could you use Clone(), and then implement the deep-copy how you need it?
您想通过引用传递。您的方法当前正在按值传递,这意味着正在复制变量的值。该方法将始终使用数据的副本。
要通过引用传递,请执行以下操作:
然后,您必须使用 ref 关键字调用该方法,如下所示:
You want to pass by reference. Your methods are currently passing by value, meaning the value of your variables are being copied. The method will always be working with a copy of the data.
To pass by reference do the following:
You will then have to call the method with the ref keyword like so: