Introduction
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’ I know recently, thanks to any comments in my first article.
These two modes of cloning, I believe, are very complicated for explaining in an article of similar characteristics and we should not try to reinvented the wheel. I have found two fantastic projects Opend Source in Jit Hub and Nuget for we need to get the job done comfortably: Nuclex and CloneExtensions.
The cloning methods of Nuclex and CloneExtensions are strongly typed.
This is the first part of my article.
Example Classes
These are the example classes we use in our article, Customer
and Address
.
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 extensions method possibility. Including Field or Property Copy too.
INTALLATION
We will install from Nuget:
Reference view:
Example for Reflection
type:
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>()
{ "a@b.com", "b@c.com" },
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] = "mailchanged@aa.com";
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:
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>()
{ "a@b.com", "b@c.com" },
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] = "mailchanged@aa.com";
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 slower, because it uses a reflection part, but in practice is a very fast method.
INSTALLATION
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>()
{ "a@b.com", "b@c.com" },
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] = "mailchanged@aa.com";
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 that 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 and make it easy and nice with these fantastic packages.
Congratulations
Congratulations to Marcin Juraszek (Nuclex) and Jun Wei Lee (CloneExtensions) for your GREAT JOB.