In this article, I will explain how to successfully clone C # objects in depth in a simple and above all effective way.
The idea is to write code that will clone objects even if classes evolve.
What is the cloning of objects?
Cloning objects simply means creating a new instance and copying all the properties of a “source” object. If the “source” object contains sub-objects, it must also be copied (in depth).
Here is an example if we had to do it “by hand”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
class User { public string FirstName { get; set; } public string LastName { get; set; } } class Command { public User User { get; set; } public string Product { get; set; } } // in hand ... class Program { static void Main() { Command c1 = new Command(); c1.Product = "Car"; c1.User = new User() { FirstName = "Smith", LastName = "John" }; // make the copy (cloning) Command c2 = new Command(); c2.Product = c1.Product; c2.User = new User(); c2.User.FirstName = c1.User.FirstName; c2.User.LastName = c1.User.LastName; } } |
You will understand the principle.
This method is simple but it has a big problem: each time the Command or User classes evolve, it is necessary to update the code that performs the cloning. This type of behavior does not respect the OCP principle and will therefore increase maintenance.
The practical solution to use
To correct this, I propose a simple trick for cloning serializable objects (you will have to add the [Serializable] attribute on your classes).
It uses the serialization / deserialization mechanisms to build a new copied object.
Here is the code of the copy method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public static T Clone<T>(T source) { if (!typeof(T).IsSerializable) { throw new ArgumentException("The type must be serializable.", "source"); } // Don't serialize a null object, simply return the default for that object if (Object.ReferenceEquals(source, null)) { return default(T); } IFormatter formatter = new BinaryFormatter(); Stream stream = new MemoryStream(); using (stream) { formatter.Serialize(stream, source); stream.Seek(0, SeekOrigin.Begin); return (T)formatter.Deserialize(stream); } } |
Then, to use it, just call the method like this:
1 2 3 |
Commande c3 = ObjectCopier.Clone(c1); |
This technique is very powerful and easy to implement. It also allows you to clone without the need to write the code to copy each property. Moreover, it makes it possible to adapt automatically to changes in class (OCP principle).