Ответ 1
Вы можете клонировать массив, который делает его копию:
int[,] originalValues = (int[,])this.Metrics.Clone();
Я передаю двумерный массив как свойство своему пользовательскому элементу управления. Там я храню эти значения в другом двумерном массиве:
int[,] originalValues = this.Metrics;
Позже я изменю значения в this.Metrics
. this.Metrics
. Но теперь, если я получаю значения из originalValues, я получаю измененные значения из this.Metrics
. Как сделать копию элементов this.Metrics
а не просто получить ссылку на массив?
Вы можете клонировать массив, который делает его копию:
int[,] originalValues = (int[,])this.Metrics.Clone();
Я не знаю, откуда у меня это получилось, но это хорошо работает для меня.
public static class GenericCopier<T> //deep copy a list
{
public static T DeepCopy(object objectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
Суть вашей проблемы здесь:
Там я храню эти значения в другом двумерном массиве
Это фактически неточно. Вы не создаете новый массив; вы устанавливаете переменную originalValues
в тот же массив. Более подробное объяснение см. Ниже.
Путаница, выраженная в комментариях к Pieter answer, вызвана некоторой неопределенностью, связанной с термином "глубокая копия".
Когда дело доходит до копирования объектов, происходит глубокое копирование и мелкое копирование.
Глубокое копирование включает в себя создание копии всех данных, принадлежащих объекту, а это означает, что если объект включает в себя элементы, которые сами по себе являются сложными (например, экземпляры определяемых пользователем ссылочных типов) эти объекты также должны быть глубоко скопированы (вместе со всеми их членами и т.д.).
Неверное копирование подразумевает простое копирование всех полей из одного объекта в другой, что означает, что если объект содержит ссылочные типы, нужно копировать только ссылки (и, следовательно, скопированные ссылки будут указывая на те же объекты).
В случае кода, который вы опубликовали:
int[,] originalValues = this.Metrics;
... на самом деле не копируется никаких объектов вообще. Все, что вы сделали, копирует одну ссылку, присваивая значение this.Metrics
(ссылку) переменной originalValues
(также ссылку на тот же массив). Это по существу то же самое, что и простое присвоение значений, например:
int x = y; // No objects being copied here.
Теперь метод Array.Clone
делает, по сути, мелкую копию. Но, как отметил Питер, нет никакой разницы между "мелкой" или "глубокой" копией массива целых чисел, поскольку целые числа не являются сложными объектами.
Если у вас есть что-то вроде этого:
StringBuilder[,] builders = GetStringBuilders();
StringBuilder[,] builderCopies = (StringBuilder[,])builders.Clone();
..., у вас будет целый новый массив (копия, да), но одна из них содержит все те же объекты StringBuilder
(так что мелкая копия). Здесь происходит глубокое и мелкое копирование; если вам нужен новый массив, содержащий копии всех объектов StringBuilder
из builders
, вам нужно сделать глубокую копию.
Если объект, который вы копируете, является массивом, вы можете использовать:
Array.Copy(sourceArray, destinationArray, sourceArray.Count)
Это даст вам отдельную копию исходного массива в ваш целевой массив.
Если вы хотите глубоко скопировать массив ссылочных типов, вы можете сделать эту методологию:
Внедрите IClonable
iterface для своего класса и сделайте свою глубокую копию всех введенных значений внутри внутри другого построенного объекта.
class A: ICloneable {
int field1;
public object Clone()
{
A a= new A();
//copy your fields here
a.field1 = this.field1;
...
}
}
Затем вы можете сделать фактическую копию, используя
A[] array1 = new A[]{....};
A[] array2 = array1.Select(a => a.Clone()).ToList();
IClonable - это здорово, но если вы не будете использовать IClonable
каждого типа в вашем клонированном типе верхнего уровня, вы получите ссылки, AFAIK.
Исходя из этого, если вы не хотите обойти объект и клонировать каждый объект внутри, это кажется самым простым подходом.
Это просто и гарантирует чистый разрыв от ссылок на глубокие объекты в оригинале:
using Newtonsoft.Json;
private T DeepCopy<T>(object input) where T : class
{
var copy = JsonConvert.SerializeObject((T)input); // serialise to string json object
var output = JsonConvert.DeserializeObject<T>(copy); // deserialise back to poco
return output;
}
Использование:
var x = DeepCopy<{ComplexType}>(itemToBeCloned);
Где ComplexType
- это все, что хочет отдохнуть от ссылок.
Он принимает любой тип, преобразует его в строку, а затем удаляет из строки новую копию.
Пример наилучшего использования: если вы выбрали сложный тип в результате лямбда-запроса и хотите изменить результат, не затрагивая оригинал.
Вы можете глубоко скопировать 1-мерный массив с помощью LINQ.
var array = Enumerable.Range(0, 10).ToArray();
var array2 = array.Select(x => x).ToArray();
array2[0] = 5;
Console.WriteLine(array[0]); // 0
Console.WriteLine(array2[0]); // 5
С массивом 2d это не будет работать, потому что 2d-массив не реализует IEnumerable.
Вам нужно создать новый массив. Затем вам нужно вручную скопировать значение каждого элемента в новый массив. То, что вы делаете в приведенном примере, - это создать две переменные массива, которые ссылаются на один и тот же массив.
Проблема с методом клонирования заключается в том, что это мелкая копия. В этом isntance, потому что вы используете int
, он не работает. Однако, если у вас был массив классов, определение интерфейса ICLonable оставляет его двусмысленным в отношении того, насколько глубоко будет клонировать.
Представьте, что у вас есть класс, который имеет свойства, которые являются другими классами, которые имеют свойства, которые являются другими классами. Клонируемый интерфейс не указывает, будет ли он клонировать суб-элементы или нет. Более того, у многих людей разные взгляды на то, что ожидаемое поведение.
Следовательно, поэтому часто рекомендуется определять два интерфейса: IShallowCopy
и IDeepCopy
.
Вот быстрое решение, где у вас есть классы POCO, которые содержат только ссылочные значения.
public class MyPoco {
public int x { get; set; }
public int y { get; set; }
public int z { get; set; }
// Add a "Clone" method.
public MyPoco Clone() {
return (MyPoco)this.MemberwiseClone();
}
}
Затем используйте LINQ для создания нового массива клонов:
var myClone = MyPocoArray.Select(x => x.Clone).ToArray();