I miei due centesimi su Deep copy vs Shallow copy in .Net

Microsoft .Net fornisce il supporto per la clonazione di oggetti, la capacità di creare una copia esatta di un oggetto (noto anche come clone). La clonazione può essere di due tipi: copia superficiale e copia profonda. Sebbene il primo possa essere implementato effettuando una chiamata al metodo MemberwiseClone della classe System.Object, l'implementazione del secondo è un po 'complicato in quanto per impostazione predefinita non è supportato nel framework. In sostanza, mentre una copia superficiale copia i riferimenti senza gli oggetti referenziati, un clone profondo crea una copia dell'oggetto sorgente insieme ai suoi riferimenti.

Quali sono tutte le opzioni disponibili per la clonazione?

Per clonare un'istanza di una classe in C #, sono disponibili alcune opzioni tra cui scegliere. Questi includono quanto segue:

  • Utilizzo del metodo System.Object.MemberwiseClone per eseguire una copia superficiale
  • Utilizzo di Reflection sfruttando il metodo Activator.CreateInstance
  • Utilizzo della serializzazione
  • Implementando l'interfaccia IClonable

Notare che quando si clonano oggetti o istanze di classi in .Net, non è necessario considerare membri statici o campi statici. Il motivo è che gli oggetti statici sono archiviati in una posizione di memoria condivisa e si dispone di una posizione di memoria allocata per ogni dominio dell'applicazione.

Copia superficiale e copia profonda

Considera una classe Employee e creiamo un'istanza della classe Employee come mostrato di seguito.

Employee emp = new Employee();

Employee clone = emp;

Fare riferimento allo snippet di codice sopra. L'operatore di assegnazione "=" copia il riferimento e non l'oggetto effettivo. Il metodo MemberwiseClone () definito nella classe System.Object fa esattamente la stessa cosa. Questi sono esempi di copia superficiale. Quindi, quando usi un operatore di assegnazione per copiare e obiettare a un altro o, usa il metodo Memberwise.Clone (), stai effettivamente facendo una copia superficiale dell'oggetto.

Mentre nella copia superficiale i membri dell'oggetto copiato fanno riferimento allo stesso oggetto dell'oggetto originale, in una copia completa, istanze separate di ciascuno dei membri del tipo di riferimento nell'istanza originale vengono create nell'istanza nuova o clonata. Pertanto, se si dispone di un tipo di riferimento nell'istanza originale, la nuova istanza conterrà anche lo stesso membro del tipo di riferimento ma questo tipo di riferimento punterà a un'istanza completamente nuova.

Nella copia superficiale, viene creato un nuovo oggetto e quindi i membri non statici dell'oggetto di origine vengono copiati nell'oggetto di destinazione o nel nuovo oggetto. Se il membro è un campo di tipo valore, viene eseguita una copia bit per bit del campo. Al contrario, se il membro da copiare è un tipo di riferimento, il riferimento viene copiato. Quindi, il membro di riferimento all'interno dell'oggetto originale e gli oggetti di destinazione si riferiscono allo stesso oggetto nella memoria.

Se si dispone di una raccolta con singoli elementi all'interno e si desidera eseguire una copia superficiale dell'istanza della raccolta. Va notato che una copia superficiale di un'istanza di raccolta copia la struttura della raccolta ma non gli elementi all'interno della raccolta. Quindi, dopo aver eseguito una copia superficiale dell'istanza della raccolta, avresti due raccolte che condividono i singoli elementi della raccolta. Al contrario, se esegui una copia completa dell'istanza della raccolta, avresti due istanze della raccolta con i singoli elementi della raccolta originale duplicati.

Implementazione della copia profonda tramite serializzazione

Puoi implementare il deep copy in molti modi. Uno dei modi più preferiti per implementare una copia completa di un oggetto è usare la serializzazione. Puoi anche sfruttare la riflessione per eseguire una copia completa di un'istanza di una classe. Il frammento di codice seguente illustra come scrivere un metodo che implementa la serializzazione binaria per eseguire una copia completa di un'istanza utilizzando C #.

public static T DeepCopy(T obj)

       {

           if (!typeof(T).IsSerializable)

           {

               throw new Exception("The source object must be serializable");

           }

           if (Object.ReferenceEquals(obj, null))

           {

               throw new Exception("The source object must not be null");

           }

           T result = default(T);

           using (var memoryStream = new MemoryStream())

           {

                var formatter = new BinaryFormatter();

               formatter.Serialize(memoryStream, obj);

               memoryStream.Seek(0, SeekOrigin.Begin);

               result = (T)formatter.Deserialize(memoryStream);

               memoryStream.Close();

           }

           return result;

       }

Considerando che hai una classe di entità chiamata Employee, puoi eseguire una copia completa di un'istanza della classe Employee come mostrato nello snippet di codice di seguito.

static void Main(string[] args)

       {

           Employee emp = new Employee();

           emp.EmployeeId = 1;

           emp.FirstName = "Joydip";

           emp.LastName = "Kanjilal";

           Employee clone = DeepCopy(emp);

           if(Object.ReferenceEquals(emp, clone))

           {

               Console.WriteLine("References are the same.");

           }

           else

           {

               Console.WriteLine("References are different.");

           }

       }

Quando si esegue il programma precedente, verrà eseguita una copia completa dell'istanza "emp" e verrà visualizzato il messaggio "I riferimenti sono diversi". sarà mostrato.