Cloning Objects In .NET - Part Two

I have decided to do a second chapter of cloning objects in .NET, because in the first chapter, I did not include ‘Reflection Mode’ and ‘Expression Tree Mode’, which I got to know recently.

These two modes of cloning, I believe, are very complicated to explain in an article of similar characteristics and we should not try to reinvent the wheel. I have found two fantastic projects Opend Source in Jit Hub and Nuget, which are required to get the job done comfortably: Nuclex and CloneExtensions.

The cloning methods of Nuclex and CloneExtensios are strong types.

Example class

This is a variant of my Customer and Address classes. 

  1. public class Customer  
  2. {  
  3.     public int                ID            { get; set; }  
  4.     public string             Name          { get; set; }  
  5.     public decimal            Sales         { get; set; }  
  6.     public DateTime           EntryDate     { get; set; }  
  7.     public Address            Adress        { get; set; }  
  8.    
  9.     public Collection<string> Mails         { get; set; }  
  10.     public List<Address>      Adresses      { get; set; }  
  11.    
  12.     protected string          Data1         { get; set; }  
  13.     private string            Data2         { get; set; }  
  14.    
  15.     public string             ReadOnlyField { get; set; }  
  16.    
  17.    
  18.     public Customer()  
  19.     {  
  20.         Data1         = "data1";  
  21.         Data2         = "Data2";  
  22.         ReadOnlyField = "readonly_data";  
  23.     }  
  24.    
  25. }  
  26.   
  27. public class Address  
  28. {  
  29.     public string Street  { get; set; }  
  30.     public string City    { get; set; }  
  31.     public int    ZipCode { get; set; }  
  32. }   

Nuclex

Nuclex.Cloning is a very comprehensive cloning library. It has two cloning modes for Reflection and Expressions Trees. It has deep and shallow copy with the extensions method possibility including Field or Property Copy too.

INTALATION

We will install from NuGet.


Reference view


Example for Reflection type is given below.

  1. using System;  
  2. using Microsoft.VisualStudio.TestTools.UnitTesting;  
  3. using Nuclex.Cloning;  
  4. using CloneLib;  
  5. using System.Collections.Generic;  
  6. using System.Collections.ObjectModel;  
  7.    
  8. namespace Clone.Tests  
  9. {  
  10.     [TestClass]  
  11.     public class NuclexReflectionTests  
  12.     {  
  13.         [TestMethod]  
  14.         public void NuclexReflectorClone()  
  15.         {  
  16.             var customer1 = new Customer  
  17.             {  
  18.                 ID        = 1,  
  19.                 Name      = "Test",  
  20.                 EntryDate = DateTime.Today,  
  21.                 Sales     = 1000m,  
  22.    
  23.                 Adress = new Address { Street  = "One street", City    = "City", ZipCode = 2222 },  
  24.    
  25.                 Mails = new Collection<string>() { "a@b.com""b@c.com" },  
  26.    
  27.                 Adresses = new List<Address>  
  28.                 {  
  29.                     new Address { City = "aaa", Street = "bbb", ZipCode = 111  },  
  30.                     new Address { City = "ddd", Street = "eee", ZipCode = 222  }  
  31.                 }  
  32.             };  
  33.    
  34.             var cloneCustomer = ReflectionCloner.DeepFieldClone<Customer>(customer1);  
  35.             /// ******  We have other posibilities, for diferents clone depths  
  36.             //var cloneCustomer = ReflectionCloner.DeepPropertyClone    (customer1);  
  37.             //var cloneCustomer = ReflectionCloner.ShallowFieldClone    (customer1);  
  38.             //var cloneCustomer = ReflectionCloner.ShallowPropertyClone (customer1);  
  39.    
  40.             cloneCustomer.Adress.City = "New city";  
  41.    
  42.             Assert.AreNotEqual(customer1, cloneCustomer);  
  43.    
  44.             Assert.AreEqual(customer1.ID       , cloneCustomer.ID);  
  45.             Assert.AreEqual(customer1.Name     , cloneCustomer.Name);  
  46.             Assert.AreEqual(customer1.EntryDate, cloneCustomer.EntryDate);  
  47.             Assert.AreEqual(customer1.Sales    , cloneCustomer.Sales);  
  48.    
  49.             Assert.AreEqual(customer1.Mails[0], cloneCustomer.Mails[0]);  
  50.             Assert.AreEqual(customer1.Mails[1], cloneCustomer.Mails[1]);  
  51.    
  52.             Assert.AreEqual(customer1.Adresses[0].City   , cloneCustomer.Adresses[0].City);  
  53.             Assert.AreEqual(customer1.Adresses[0].Street , cloneCustomer.Adresses[0].Street);  
  54.             Assert.AreEqual(customer1.Adresses[0].ZipCode, cloneCustomer.Adresses[0].ZipCode);  
  55.             Assert.AreEqual(customer1.Adresses[1].City   , cloneCustomer.Adresses[1].City);  
  56.             Assert.AreEqual(customer1.Adresses[1].Street , cloneCustomer.Adresses[1].Street);  
  57.             Assert.AreEqual(customer1.Adresses[1].ZipCode, cloneCustomer.Adresses[1].ZipCode);  
  58.    
  59.             Assert.AreEqual(customer1.ReadOnlyField, cloneCustomer.ReadOnlyField);  
  60.    
  61.             /// Change values in clone object for validate objects don't linked  
  62.             cloneCustomer.Adress.City = "Changed City";  
  63.             Assert.AreNotEqual(customer1.Adress.City, cloneCustomer.Adress.City);  
  64.    
  65.             cloneCustomer.Mails[0] = "mailchanged@aa.com";  
  66.             Assert.AreNotEqual(customer1.Mails[0], cloneCustomer.Mails[0]);  
  67.    
  68.             cloneCustomer.Adresses[0].Street = "New Street";  
  69.             Assert.AreNotEqual(customer1.Adresses[0].Street, cloneCustomer.Adresses[0].Street);  
  70.         }  
  71.     }  
  72. }   

Example for ExpressionTrees type is given below.

  1. using System;  
  2. using Microsoft.VisualStudio.TestTools.UnitTesting;  
  3. using Nuclex.Cloning;  
  4. using CloneLib;  
  5. using System.Collections.Generic;  
  6. using System.Collections.ObjectModel;  
  7.    
  8. namespace Clone.Tests  
  9. {  
  10.     [TestClass]  
  11.     public class NeclexExpressionTreeClonerText  
  12.     {  
  13.         [TestMethod]  
  14.         public void NuclexReflectorClone()  
  15.         {  
  16.             var customer1 = new Customer  
  17.             {  
  18.                 ID = 1,  
  19.                 Name = "Test",  
  20.                 EntryDate = DateTime.Today,  
  21.                 Sales = 1000m,  
  22.    
  23.                 Adress = new Address { Street = "One street", City = "City", ZipCode = 2222 },  
  24.    
  25.                 Mails = new Collection<string>() { "a@b.com""b@c.com" },  
  26.    
  27.                 Adresses = new List<Address>  
  28.                 {  
  29.                     new Address { City = "aaa", Street = "bbb", ZipCode = 111  },  
  30.                     new Address { City = "ddd", Street = "eee", ZipCode = 222  }  
  31.                 }  
  32.             };  
  33.    
  34.             var cloneCustomer = ExpressionTreeCloner.DeepFieldClone<Customer>(customer1);  
  35.             /// ******  We have other posibilities, for diferents clone depths  
  36.             //var cloneCustomer = ExpressionTreeCloner.DeepPropertyClone    (customer1);  
  37.             //var cloneCustomer = ExpressionTreeCloner.ShallowFieldClone    (customer1);  
  38.             //var cloneCustomer = ExpressionTreeCloner.ShallowPropertyClone (customer1);  
  39.    
  40.             cloneCustomer.Adress.City = "New city";  
  41.    
  42.             Assert.AreNotEqual(customer1, cloneCustomer);  
  43.    
  44.             Assert.AreEqual(customer1.ID       , cloneCustomer.ID);  
  45.             Assert.AreEqual(customer1.Name     , cloneCustomer.Name);  
  46.             Assert.AreEqual(customer1.EntryDate, cloneCustomer.EntryDate);  
  47.             Assert.AreEqual(customer1.Sales    , cloneCustomer.Sales);  
  48.    
  49.             Assert.AreEqual(customer1.Mails[0], cloneCustomer.Mails[0]);  
  50.             Assert.AreEqual(customer1.Mails[1], cloneCustomer.Mails[1]);  
  51.    
  52.             Assert.AreEqual(customer1.Adresses[0].City   , cloneCustomer.Adresses[0].City);  
  53.             Assert.AreEqual(customer1.Adresses[0].Street , cloneCustomer.Adresses[0].Street);  
  54.             Assert.AreEqual(customer1.Adresses[0].ZipCode, cloneCustomer.Adresses[0].ZipCode);  
  55.             Assert.AreEqual(customer1.Adresses[1].City   , cloneCustomer.Adresses[1].City);  
  56.             Assert.AreEqual(customer1.Adresses[1].Street , cloneCustomer.Adresses[1].Street);  
  57.             Assert.AreEqual(customer1.Adresses[1].ZipCode, cloneCustomer.Adresses[1].ZipCode);  
  58.    
  59.             Assert.AreEqual(customer1.ReadOnlyField, cloneCustomer.ReadOnlyField);  
  60.    
  61.             /// Change values in clone object for validate objects don't linked  
  62.             cloneCustomer.Adress.City = "Changed City";  
  63.             Assert.AreNotEqual(customer1.Adress.City, cloneCustomer.Adress.City);  
  64.    
  65.             cloneCustomer.Mails[0] = "mailchanged@aa.com";  
  66.             Assert.AreNotEqual(customer1.Mails[0], cloneCustomer.Mails[0]);  
  67.    
  68.             cloneCustomer.Adresses[0].Street = "New Street";  
  69.             Assert.AreNotEqual(customer1.Adresses[0].Street, cloneCustomer.Adresses[0].Street);  
  70.         }  
  71.     }  
  72. }   

It is quite simple and it has many possibilities.

CloneExtensions

Clone Extensions is a very good cloning library. It is a faster solution, because it is based in Expressions Trees. According to their documentation, the first execution for type is more slow, because it uses a reflection part, but in practice, it is a very fast method.

INSTALATION

We will install from NuGet:


Reference view


Example of CloneExtensions 

  1. using System;  
  2. using Microsoft.VisualStudio.TestTools.UnitTesting;  
  3. using CloneLib;  
  4. using System.Collections.ObjectModel;  
  5. using System.Collections.Generic;  
  6. using CloneExtensions;  
  7.    
  8. namespace Clone.Tests  
  9. {  
  10.     [TestClass]  
  11.     public class CloneExtensionsTests  
  12.     {  
  13.         [TestMethod]  
  14.         public void NuclexReflectorClone()  
  15.         {  
  16.             var customer1 = new Customer  
  17.             {  
  18.                 ID        = 1,  
  19.                 Name      = "Test",  
  20.                 EntryDate = DateTime.Today,  
  21.                 Sales     = 1000m,  
  22.    
  23.                 Adress = new Address { Street = "One street", City = "City", ZipCode = 2222 },  
  24.    
  25.                 Mails = new Collection<string>() { "a@b.com""b@c.com" },  
  26.    
  27.                 Adresses = new List<Address>  
  28.                 {  
  29.                     new Address { City = "aaa", Street = "bbb", ZipCode = 111  },  
  30.                     new Address { City = "ddd", Street = "eee", ZipCode = 222  }  
  31.                 }  
  32.             };  
  33.    
  34.             var cloneCustomer = customer1.GetClone();  
  35.    
  36.             cloneCustomer.Adress.City = "New city";  
  37.    
  38.             Assert.AreNotEqual(customer1, cloneCustomer);  
  39.    
  40.             Assert.AreEqual(customer1.ID        , cloneCustomer.ID);  
  41.             Assert.AreEqual(customer1.Name      , cloneCustomer.Name);  
  42.             Assert.AreEqual(customer1.EntryDate , cloneCustomer.EntryDate);  
  43.             Assert.AreEqual(customer1.Sales     , cloneCustomer.Sales);  
  44.    
  45.             Assert.AreEqual(customer1.Mails[0], cloneCustomer.Mails[0]);  
  46.             Assert.AreEqual(customer1.Mails[1], cloneCustomer.Mails[1]);  
  47.    
  48.             Assert.AreEqual(customer1.Adresses[0].City   , cloneCustomer.Adresses[0].City);  
  49.             Assert.AreEqual(customer1.Adresses[0].Street , cloneCustomer.Adresses[0].Street);  
  50.             Assert.AreEqual(customer1.Adresses[0].ZipCode, cloneCustomer.Adresses[0].ZipCode);  
  51.             Assert.AreEqual(customer1.Adresses[1].City   , cloneCustomer.Adresses[1].City);  
  52.             Assert.AreEqual(customer1.Adresses[1].Street , cloneCustomer.Adresses[1].Street);  
  53.             Assert.AreEqual(customer1.Adresses[1].ZipCode, cloneCustomer.Adresses[1].ZipCode);  
  54.    
  55.             Assert.AreEqual(customer1.ReadOnlyField, cloneCustomer.ReadOnlyField);  
  56.    
  57.             /// Change values in clone object for validate objects don't linked  
  58.             cloneCustomer.Adress.City = "Changed City";  
  59.             Assert.AreNotEqual(customer1.Adress.City, cloneCustomer.Adress.City);  
  60.    
  61.             cloneCustomer.Mails[0] = "mailchanged@aa.com";  
  62.             Assert.AreNotEqual(customer1.Mails[0], cloneCustomer.Mails[0]);  
  63.    
  64.             cloneCustomer.Adresses[0].Street = "New Street";  
  65.             Assert.AreNotEqual(customer1.Adresses[0].Street, cloneCustomer.Adresses[0].Street);  
  66.         }  
  67.     }  
  68. }   

It’s another solution, which is very fast and very easy.

Conclusion

If you need to clone some object and if you don’t need an explicit copy, these two libraries are your answer. You don’t try to reinvent the wheel, as you can easily perform cloning of an object, as it is nice with fantastic packages.