Правильное копирование данных в 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;
При этом * Точки являются экземплярами Точек, которые имеют несколько примитивных (двойных) и не примитивных (Вектор) типов данных.
Я сталкиваюсь с (очевидной) проблемой, заключающейся в том, что приведенный выше код, скорее всего, просто установит 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
}
3 ответа
Это одна из самых неприятных проблем с дизайном C# ИМХО.
Если 'Point' является структурой (значением), то будет сделана копия для каждого элемента, поэтому x = y
сделает независимую копию у. Но если это класс (ссылка), x = y
просто укажет ссылку x на то же хранилище, которое используется для y, так что они просто станут разными псевдонимами для одних и тех же данных.
Я знаю два решения вашей проблемы:
Используйте структуру. Это даст вам поведение типа значения, которое вы ожидаете от математических классов. Чтобы сохранить эффективность кода, вам, возможно, потребуется передавать ссылки везде, чтобы избежать непрерывного копирования структур.
Используйте класс, но будьте очень и очень осторожны при использовании =, чтобы убедиться, что вы сохраняете независимую копию данных. Вам нужно будет изменить
x = y
к чему-то другому, напримерx = new Point(y);
,
Не могли бы вы использовать Clone(), а затем реализовать глубокое копирование, как вам это нужно?
StartPoint = EndPoint;
EndPoint = (Point)CurrentPoint.Clone();
Вы хотите пройти по ссылке. Ваши методы в настоящее время передаются по значению, что означает, что значения ваших переменных копируются. Метод всегда будет работать с копией данных.
Для перехода по ссылке сделайте следующее:
public void predict(ref Point start, ref Point end)
{
end.velocity = start.velocity + dt * end.acceleration;
end.position = start.position + dt * end.velocity;
}
Затем вам нужно будет вызвать метод с ключевым словом ref следующим образом:
predict(ref start, ref end);