Как сделать поверхностную копию массива? - PullRequest
17 голосов
/ 29 октября 2010

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

int[,] originalValues = this.Metrics;

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

Ответы [ 9 ]

31 голосов
/ 29 октября 2010

Я не знаю, откуда я это взял, но у меня это хорошо работает.

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);
            }
        }
    }
29 голосов
/ 29 октября 2010

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

int[,] originalValues = (int[,])this.Metrics.Clone();
11 голосов
/ 29 октября 2010

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

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

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


Путаница, выраженная в комментариях к ответу Питера , связана с некоторой неопределенностью, связанной с термином "глубокая копия".

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

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

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

В случае кода, который вы разместили:

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

9 голосов
/ 29 октября 2010

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

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

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

2 голосов
/ 10 мая 2018

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 - это все, что хочет отдохнуть от ссылок.

Он принимает любой Тип, преобразует его в строку, а затем удаляет из строки в новую копию.

Пример наилучшего использования: Если вы выбрали сложный тип в результате лямбда-запроса и хотите изменить результат, не затрагивая оригинал.

2 голосов
/ 24 июня 2017

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

Реализовать IClonable интерфейс для вашего класса и сделать полную копию всех полей с типизированным значением внутри другого созданного объекта..

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();
1 голос
/ 18 января 2017

Вы можете выполнить глубокое копирование массива 1d с помощью 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 голосов
/ 01 июля 2019

Вот быстрое решение, которое почти похоже на некоторые ответы здесь, но в котором упоминается MemberwiseClone .

У меня есть 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();
0 голосов
/ 29 октября 2010

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...