2010-10-29 32 views
14

Ho passato un array bidimensionale come proprietà al mio controllo utente. Ci devo conservare questi valori in un altro array bidimensionale:Come fare una copia profonda di un array?

int[,] originalValues = this.Metrics; 

Più tardi ho cambiare i valori in this.Metrics. Ma ora se recupero i valori da originalValues, ottengo i valori modificati da this.Metrics. Come posso creare una copia profonda e non copiare semplicemente i riferimenti in C#?

+1

Nel caso qualcuno inciampare qui, ora c'e 'Array.Clone()'. (.net 4.5) – Jeevaka

+2

@Jeevaka Questa domanda si riferisce in particolare alla creazione di una copia _deep_ di un array. Secondo [MSDN docs] (https://msdn.microsoft.com/en-us/library/system.array.clone (v = vs.110) .aspx), 'Array.Clone() '" Crea una ** copia ** superficiale della matrice. " (enfasi aggiunta). – jmbpiano

+1

@ jmbpiano: Beh, se ho ragione :, 'int sono memorizzati per valore, non per riferimento. Quello che succederebbe qui è che Array.Clone() creerebbe un nuovo oggetto Array e copierà qualsiasi cosa nella memoria a cui fa riferimento il riferimento "this.Matrices". Poiché i contenuti dell'array non sono riferimenti, la nuova copia non condividerebbe nulla con l'originale. Se questi non fossero "int" ma qualcosa derivato da "oggetto", la tua argomentazione sarebbe corretta. Immagino che possiamo sempre provare e vedere. – Jeevaka

risposta

23

È possibile clonare un array, che fa una copia di esso:

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

Il clone non esegue una copia profonda (tranne che nei casi banali di matrici di tipo valore). –

+11

Piccolo segreto: la domanda riguarda un array int. Nient'altro che questo sarebbe abbastanza eccessivo. –

+2

lol, stavo solo rispondendo alla domanda generale. Perché chiedere una copia profonda di un tipo di valore? –

0

È necessario creare un nuovo array. È quindi necessario copiare manualmente il valore di ciascun elemento nel nuovo array. Quello che stai facendo nell'esempio dato è quello di creare due variabili di matrice che fanno riferimento allo stesso array.

Il problema con il metodo clone è che si tratta di una copia poco profonda. In questa isntance, perché stai usando int, non importa. Ovunque, se tu avessi una serie di classi la definizione dell'interfaccia ICLonable lascia ambigua quanto profonda sarà il clone.

Immaginate se aveste una classe che ha proprietà che sono altre classi che ha proprietà che sono altre classi. L'interfaccia clonabile non indica se clonerà o meno i membri secondari. Inoltre, molte persone hanno opinioni diverse su quale sia il comportamento previsto.

Ecco perché è spesso consigliabile definire due interfacce, IShallowCopy e IDeepCopy.

7

Se l'oggetto che si sta copiando è un array, quindi è possibile utilizzare:

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

questo vi darà una copia separata della matrice originale nella vostra matrice di destinazione.

28

Non so da dove ho preso questo, ma questo funziona bene per me.

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

Questo è un po 'più costoso della risposta Array.Copy di Brian Scott – Nicholas

+5

Definire questo overkill è come chiamare un "boom" di supernova: D –

+0

Un altro modo per clonare L'elenco è il seguente \ n var clonedList = new List (sampleList); – Navap

6

Il nocciolo del problema è qui:

Ci devo conservare questi valori in un altro array bidimensionale

Questo è in realtà imprecisa. Non stai creando un nuovo array; si imposta la variabile originalValues su stesso matrice. Per una spiegazione più dettagliata, vedi sotto.


La confusione espresso nei commenti per Pieter's answer è dovuto a qualche incertezza che circonda il termine "copia completa".

Quando si tratta di copiare oggetti, c'è una copia profonda e una copia superficiale.

copia profonda consiste nel fare una copia di tutti i dati appartenenti a un oggetto, il che significa che se l'oggetto include componenti che sono essi stessi complessi (per esempio, esempi di tipi di riferimento definiti dall'utente), tali gli oggetti devono essere copiati in profondità (insieme a tutti gli membri e così via).

Shallow copia comporta semplicemente copiando tutti i campi da un oggetto all'altro, il che significa che se l'oggetto include tipi di riferimento, solo i riferimenti devono essere copiati (e quindi i riferimenti copiati verranno rivolte alla stessa oggetti).

Nel caso del codice che hai postato:

int[,] originalValues = this.Metrics; 

... c'è in realtà non la copia di tutti gli oggetti a tutti. Tutto ciò che hai fatto è stato copiato un singolo riferimento, assegnando il valore di this.Metrics (un riferimento) alla variabile originalValues (anch'essa un riferimento, allo stesso array). Questo è essenzialmente lo stesso come un semplice assegnazione valore, in questo modo:

int x = y; // No objects being copied here. 

Ora, il metodo Array.Clone rende, di fatto, una superficiale copia . Ma come ha sottolineato Pieter, non c'è davvero alcuna differenza tra una copia "superficiale" o "profonda" di un array di numeri interi , poiché gli interi non sono oggetti complessi.

Se tu avessi qualcosa di simile:

StringBuilder[,] builders = GetStringBuilders(); 
StringBuilder[,] builderCopies = (StringBuilder[,])builders.Clone(); 

..., che ci si finisce con una nuova gamma (una copia, sì), ma uno che contiene tutti gli stessi oggetti StringBuilder (quindi una copia superficiale). È qui che entra in gioco la copia profonda o poco profonda; se si desiderava un nuovo array contenente copie di copie da builders, è necessario eseguire una copia profonda.

+0

Bene, 'int x = y;' copia _qualcosa_; int è immutabile e assegnandolo a una nuova variabile creerà sempre una copia. Ma sì, questo è essenzialmente ciò che accade qui, solo che il valore copiato è il _reference_ dell'array, non l'array stesso. – Nyerguds

0

È possibile eseguire la deepcopy di un array 1d utilizzando 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 

Con array 2d, questo non funziona perché l'array 2d non implementa IEnumerable.

0

Se si desidera copia completa una serie di tipi di riferimento, si può fare questa metodologia:

Implementare IClonable iterface per la classe e fare la vostra copia completa di tutti i campi di valore digitato all'interno di un altro oggetto costruito.

class A: ICloneable { 
    int field1; 
    public object Clone() 
     { 
      A a= new A(); 
      //copy your fields here 
      a.field1 = this.field1; 
      ... 
     } 
} 

Poi si può fare la copia reale utilizzando

A[] array1 = new A[]{....}; 
A[] array2 = array1.Select(a => a.Clone()).ToList(); 
Problemi correlati