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.
- public class Customer
- {
- public int ID { get; set; }
- public string Name { get; set; }
- public decimal Sales { get; set; }
- public DateTime EntryDate { get; set; }
- public Address Adress { get; set; }
-
- public Collection<string> Mails { get; set; }
- public List<Address> Adresses { get; set; }
-
- protected string Data1 { get; set; }
- private string Data2 { get; set; }
-
- public string ReadOnlyField { get; set; }
-
-
- public Customer()
- {
- Data1 = "data1";
- Data2 = "Data2";
- ReadOnlyField = "readonly_data";
- }
-
- }
-
- public class Address
- {
- public string Street { get; set; }
- public string City { get; set; }
- public int ZipCode { get; set; }
- }
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.
- using System;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using Nuclex.Cloning;
- using CloneLib;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
-
- namespace Clone.Tests
- {
- [TestClass]
- public class NuclexReflectionTests
- {
- [TestMethod]
- public void NuclexReflectorClone()
- {
- var customer1 = new Customer
- {
- ID = 1,
- Name = "Test",
- EntryDate = DateTime.Today,
- Sales = 1000m,
-
- Adress = new Address { Street = "One street", City = "City", ZipCode = 2222 },
-
- Mails = new Collection<string>() { "[email protected]", "[email protected]" },
-
- Adresses = new List<Address>
- {
- new Address { City = "aaa", Street = "bbb", ZipCode = 111 },
- new Address { City = "ddd", Street = "eee", ZipCode = 222 }
- }
- };
-
- var cloneCustomer = ReflectionCloner.DeepFieldClone<Customer>(customer1);
-
-
-
-
-
- cloneCustomer.Adress.City = "New city";
-
- Assert.AreNotEqual(customer1, cloneCustomer);
-
- Assert.AreEqual(customer1.ID , cloneCustomer.ID);
- Assert.AreEqual(customer1.Name , cloneCustomer.Name);
- Assert.AreEqual(customer1.EntryDate, cloneCustomer.EntryDate);
- Assert.AreEqual(customer1.Sales , cloneCustomer.Sales);
-
- Assert.AreEqual(customer1.Mails[0], cloneCustomer.Mails[0]);
- Assert.AreEqual(customer1.Mails[1], cloneCustomer.Mails[1]);
-
- Assert.AreEqual(customer1.Adresses[0].City , cloneCustomer.Adresses[0].City);
- Assert.AreEqual(customer1.Adresses[0].Street , cloneCustomer.Adresses[0].Street);
- Assert.AreEqual(customer1.Adresses[0].ZipCode, cloneCustomer.Adresses[0].ZipCode);
- Assert.AreEqual(customer1.Adresses[1].City , cloneCustomer.Adresses[1].City);
- Assert.AreEqual(customer1.Adresses[1].Street , cloneCustomer.Adresses[1].Street);
- Assert.AreEqual(customer1.Adresses[1].ZipCode, cloneCustomer.Adresses[1].ZipCode);
-
- Assert.AreEqual(customer1.ReadOnlyField, cloneCustomer.ReadOnlyField);
-
-
- cloneCustomer.Adress.City = "Changed City";
- Assert.AreNotEqual(customer1.Adress.City, cloneCustomer.Adress.City);
-
- cloneCustomer.Mails[0] = "[email protected]";
- Assert.AreNotEqual(customer1.Mails[0], cloneCustomer.Mails[0]);
-
- cloneCustomer.Adresses[0].Street = "New Street";
- Assert.AreNotEqual(customer1.Adresses[0].Street, cloneCustomer.Adresses[0].Street);
- }
- }
- }
Example for ExpressionTrees type is given below.
- using System;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using Nuclex.Cloning;
- using CloneLib;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
-
- namespace Clone.Tests
- {
- [TestClass]
- public class NeclexExpressionTreeClonerText
- {
- [TestMethod]
- public void NuclexReflectorClone()
- {
- var customer1 = new Customer
- {
- ID = 1,
- Name = "Test",
- EntryDate = DateTime.Today,
- Sales = 1000m,
-
- Adress = new Address { Street = "One street", City = "City", ZipCode = 2222 },
-
- Mails = new Collection<string>() { "[email protected]", "[email protected]" },
-
- Adresses = new List<Address>
- {
- new Address { City = "aaa", Street = "bbb", ZipCode = 111 },
- new Address { City = "ddd", Street = "eee", ZipCode = 222 }
- }
- };
-
- var cloneCustomer = ExpressionTreeCloner.DeepFieldClone<Customer>(customer1);
-
-
-
-
-
- cloneCustomer.Adress.City = "New city";
-
- Assert.AreNotEqual(customer1, cloneCustomer);
-
- Assert.AreEqual(customer1.ID , cloneCustomer.ID);
- Assert.AreEqual(customer1.Name , cloneCustomer.Name);
- Assert.AreEqual(customer1.EntryDate, cloneCustomer.EntryDate);
- Assert.AreEqual(customer1.Sales , cloneCustomer.Sales);
-
- Assert.AreEqual(customer1.Mails[0], cloneCustomer.Mails[0]);
- Assert.AreEqual(customer1.Mails[1], cloneCustomer.Mails[1]);
-
- Assert.AreEqual(customer1.Adresses[0].City , cloneCustomer.Adresses[0].City);
- Assert.AreEqual(customer1.Adresses[0].Street , cloneCustomer.Adresses[0].Street);
- Assert.AreEqual(customer1.Adresses[0].ZipCode, cloneCustomer.Adresses[0].ZipCode);
- Assert.AreEqual(customer1.Adresses[1].City , cloneCustomer.Adresses[1].City);
- Assert.AreEqual(customer1.Adresses[1].Street , cloneCustomer.Adresses[1].Street);
- Assert.AreEqual(customer1.Adresses[1].ZipCode, cloneCustomer.Adresses[1].ZipCode);
-
- Assert.AreEqual(customer1.ReadOnlyField, cloneCustomer.ReadOnlyField);
-
-
- cloneCustomer.Adress.City = "Changed City";
- Assert.AreNotEqual(customer1.Adress.City, cloneCustomer.Adress.City);
-
- cloneCustomer.Mails[0] = "[email protected]";
- Assert.AreNotEqual(customer1.Mails[0], cloneCustomer.Mails[0]);
-
- cloneCustomer.Adresses[0].Street = "New Street";
- Assert.AreNotEqual(customer1.Adresses[0].Street, cloneCustomer.Adresses[0].Street);
- }
- }
- }
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
- using System;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using CloneLib;
- using System.Collections.ObjectModel;
- using System.Collections.Generic;
- using CloneExtensions;
-
- namespace Clone.Tests
- {
- [TestClass]
- public class CloneExtensionsTests
- {
- [TestMethod]
- public void NuclexReflectorClone()
- {
- var customer1 = new Customer
- {
- ID = 1,
- Name = "Test",
- EntryDate = DateTime.Today,
- Sales = 1000m,
-
- Adress = new Address { Street = "One street", City = "City", ZipCode = 2222 },
-
- Mails = new Collection<string>() { "[email protected]", "[email protected]" },
-
- Adresses = new List<Address>
- {
- new Address { City = "aaa", Street = "bbb", ZipCode = 111 },
- new Address { City = "ddd", Street = "eee", ZipCode = 222 }
- }
- };
-
- var cloneCustomer = customer1.GetClone();
-
- cloneCustomer.Adress.City = "New city";
-
- Assert.AreNotEqual(customer1, cloneCustomer);
-
- Assert.AreEqual(customer1.ID , cloneCustomer.ID);
- Assert.AreEqual(customer1.Name , cloneCustomer.Name);
- Assert.AreEqual(customer1.EntryDate , cloneCustomer.EntryDate);
- Assert.AreEqual(customer1.Sales , cloneCustomer.Sales);
-
- Assert.AreEqual(customer1.Mails[0], cloneCustomer.Mails[0]);
- Assert.AreEqual(customer1.Mails[1], cloneCustomer.Mails[1]);
-
- Assert.AreEqual(customer1.Adresses[0].City , cloneCustomer.Adresses[0].City);
- Assert.AreEqual(customer1.Adresses[0].Street , cloneCustomer.Adresses[0].Street);
- Assert.AreEqual(customer1.Adresses[0].ZipCode, cloneCustomer.Adresses[0].ZipCode);
- Assert.AreEqual(customer1.Adresses[1].City , cloneCustomer.Adresses[1].City);
- Assert.AreEqual(customer1.Adresses[1].Street , cloneCustomer.Adresses[1].Street);
- Assert.AreEqual(customer1.Adresses[1].ZipCode, cloneCustomer.Adresses[1].ZipCode);
-
- Assert.AreEqual(customer1.ReadOnlyField, cloneCustomer.ReadOnlyField);
-
-
- cloneCustomer.Adress.City = "Changed City";
- Assert.AreNotEqual(customer1.Adress.City, cloneCustomer.Adress.City);
-
- cloneCustomer.Mails[0] = "[email protected]";
- Assert.AreNotEqual(customer1.Mails[0], cloneCustomer.Mails[0]);
-
- cloneCustomer.Adresses[0].Street = "New Street";
- Assert.AreNotEqual(customer1.Adresses[0].Street, cloneCustomer.Adresses[0].Street);
- }
- }
- }
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.