2010-10-29 2 views
14

Я передаю двумерный массив как свойство для моего пользовательского элемента управления. Там я храню эти значения в другом двумерном массиве:Как сделать глубокую копию массива?

int[,] originalValues = this.Metrics; 

Позже я изменить значения в this.Metrics. Но теперь, если я получаю значения из originalValues, я получаю измененные значения от this.Metrics. Как сделать глубокую копию и не просто скопировать ссылки в C#?

+1

В случае, если кто-то споткнется здесь, теперь есть 'Array.Clone()'. (.net 4.5) – Jeevaka

+2

@Jeevaka Этот вопрос задается вопросом о создании _deep_ копии массива. В соответствии с [документами MSDN] (https://msdn.microsoft.com/en-us/library/system.array.clone (v = vs.110) .aspx), 'Array.Clone() '" Создает ** неглубокую ** копию массива. " (выделено курсивом). – jmbpiano

+1

@ jmbpiano: Ну, если я прав :, 'int хранятся по значению, а не по ссылке. Что произойдет здесь, так это то, что Array.Clone() создаст новый объект Array и скопирует все в памяти, на которую ссылается ссылка this.Matrices. Поскольку содержимое массива не является ссылкой, новая копия не будет передавать ничего с оригиналом. Если бы они были не «int», а что-то, полученное из «объекта», ваш аргумент был бы правильным. Думаю, мы всегда можем попробовать и посмотреть. – Jeevaka

ответ

23

Вы можете клонировать массив, который делает копию:

int[,] originalValues = (int[,])this.Metrics.Clone(); 
+25

Клон не делает глубокую копию (за исключением нескольких тривиальных случаев массивов типов значений). –

+11

Маленький секрет: вопрос о массиве int. Все остальное, чем это, было бы слишком полным. –

+2

lol, я просто отвечал на общий вопрос. Зачем запрашивать глубокую копию типа значения? –

0

Вам нужно создать новый массив. Затем вам необходимо вручную скопировать значение каждого элемента в новый массив. То, что вы делаете в приведенном примере, - это создать две переменные массива, которые ссылаются на один и тот же массив.

Проблема с методом клонирования заключается в том, что это мелкая копия. В этом isntance, потому что вы используете int, это не матер. Однако, если бы у вас был массив классов, то определение интерфейса ICLonable оставляет его двусмысленным в отношении того, насколько глубоко будет клонировать.

Представьте, если у вас есть класс, который имеет свойства, которые являются другими классами, которые имеют свойства, которые являются другими классами. Клонируемый интерфейс не указывает, будет ли он клонировать суб-элементы или нет. Более того, у многих людей разные взгляды на то, что ожидаемое поведение.

Следовательно, поэтому часто рекомендуется определять два интерфейса: IShallowCopy и IDeepCopy.

7

Если объект, который вы копируете массив, то вы можете использовать:

Array.Copy(sourceArray, destinationArray, sourceArray.Count) 

Это даст вам отдельную копию исходного массива в массив назначения.

28

Я не знаю, откуда у меня это получилось, но это хорошо работает для меня.

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); 
      } 
     } 
    } 
+0

Это немного дороже, чем Array.Copy ответ Брайана Скотта – Nicholas

+5

Вызов этого излишества - это вызов сверхновой «бум»: D –

+0

Другой способ клонирования Список выглядит следующим образом \ n var clonedList = new List (sampleList); – Navap

6

Суть вашей проблемы здесь:

Там хранить эти значения в другом двумерном массиве

Это фактически неточными. Вы не создаете новый массив; вы устанавливаете переменную originalValues в такой же массив. Более подробное объяснение см. Ниже.


Путаница выражается в комментариях к Pieter's answer обусловлен некоторой неопределенности термина «глубокая копия.»

Когда дело доходит до копирования объектов, происходит глубокое копирование и мелкое копирование.

Глубокое копирование включает в себя создание копии всех данных, принадлежащих к объекту, а это означает, что если объект включает в себя элементы, которые сами по себе являются сложными (например, экземпляры определенных пользователем ссылочных типов), эти объекты также должны быть глубоко скопированы (вместе со всеми их членами и т. д.).

Shallow копирование предполагает простое копирование всех полей от одного объекта к другому, а это значит, что если объект включает в себя ссылочные типы, только ссылки должны быть скопированы (и так скопированные ссылки будут указывать на то же самое объекты).

В случае кода у вас в курсе:

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, вам нужно сделать глубокую копию.

+0

Ну, 'int x = y;' копирует _something_; int неизменен, и присвоение его новой переменной всегда создаст копию. Но да, это то, что происходит здесь, только скопированное значение - это _reference_ для массива, а не сам массив. – Nyerguds

0

Вы можете глубоко скопировать 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.

0

Если вы хотите глубокую копию массива ссылочных типов, вы можете сделать эту методику:

Implement 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(); 
Смежные вопросы