Cloning objects in any programming language is tricky, but I think in C# that’s taken up an extra notch by giving you a couple of tools that look like they would probably solve your problem, but then not letting you know that it’s likely to only solve a very slim use case and nothing else. This guide is going to be half a guide on cloning, but also a quick refresher on what “cloning” actually means in the context of C#.
Shallow Clone vs Deep Clone
It’s worth starting at the basics. What is the difference between a shallow clone and a deep clone? (Also sometimes known as a shallow copy and a deep copy).
A shallow clone is a copy of an object that duplicates as little as possible. In C# this generally means that value types are duplicated, but reference types are not. Or as an example, let’s take the following object :
class Person { public int Age { get; set; } public Person Father { get; set; } public Person Mother { get; set; } }
If I made a shallow clone of this object and changed the age, the original object would not have it’s age changed. However, if I then changed a property on the cloned object’s father, then I would also affect the original object’s father because the reference isn’t cloned.
That may be a little confusing so thinking of it another way, in C#, when you do a shallow clone of an object, you are going “one level deep”, hence shallow. Any objects inside the object you want to clone are not themselves recursively cloned as well.
A deep clone is obviously the opposite. It keeps going down the tree and tries to clone all properties of your object, then the properties of that property etc.
Memberwise Clone
So if you’ve done any research at all into cloning in C#, you’ve probably come across the “memberwise” clone method. It’s available to every class but *only inside that class* as it’s a protected method of Object. You cannot call it on an object from another class.
For example :
class Person { public string Name { get; set; } public Person Father { get; set; } public Person Mother { get; set; } public Person Clone() { return (Person)this.MemberwiseClone(); } }
However a quick look at the intellisense tells us something…
Creates a shallow copy of the current Object.
A shallow copy. Oof! So calling clone on this object will only shallow clone it, and not do a deep clone. If your object is purely primitives, then this can actually work for you and you can stop right here. But in most cases, we are looking to a deep clone. Even if for the sole reason that should someone add a property of another class here, then everything doesn’t suddenly break.
ICloneable Interface
So you might have also come across this interface called “ICloneable”. Unfortunately, it doesn’t really solve any problems an infact the documentation has this to say :
Because callers of Clone() cannot depend on the method performing a predictable cloning operation, we recommend that ICloneable not be implemented in public APIs.
Pretty stern words but it makes sense because if we look at how we might implement ICloneable.
class Person : ICloneable { public string Name { get; set; } public Person Father { get; set; } public Person Mother { get; set; } public object Clone() { return this; } }
Here we haven’t even cloned our object, we’ve just returned the same exact instance. While yes, we may expect a developer to know better, the point is that a caller doesn’t have any idea how the deep the clone goes, or if it’s even a clone at all. For this reason, people tend to stay away from the ICloneable interface.
Even if you get past that… It returns an object rather than a Person object which is annoying!
But with all that said, ICloneable doesn’t actually provide implementation details for cloning anyway. It just tells a caller “maybe call this and try your luck at getting a clone back”, but it doesn’t help a developer actually do a clone, shallow or deep.
Manually Deep Cloning
A sure fire way to know that you are deep cloning an object is to manually deep clone it yourself!
class Person { public string Name { get; set; } public Person Father { get; set; } public Person Mother { get; set; } public Person Clone() { return new Person { Name = this.Name, Father = this.Father == null ? null : new Person { Name = this.Father.Name }, Mother = this.Mother == null ? null : new Person { Name = this.Mother.Name } }; } }
In our clone method we are now creating new objects to completely clone our object and have no references to the original. This works great but it is time consuming. Any new property on this object means we have to modify our Clone method and re-test everything again.
On top of that, we are only going one level deep, for example we are not cloning our Father’s father, or our Mother’s mother.We may not need them for whatever we are then doing with our clones, but it’s also not a true “clone”. It’s just a clone of whatever we could be bothered mapping across!
With all those complaints out of the way however, it’s really not a bad option if it’s only a couple of classes that you really want to clone and they aren’t going to be modified that often.
Binary Serializer Cloning
An exceptionally common way of doing deep clones (Or atleast, more than just a shallow clone) is by serialising the data to another format and then back again.
Take this example of using a Binary Serializer on our Person class :
[Serializable] class Person { public string Name { get; set; } public Person Father { get; set; } public Person Mother { get; set; } public Person Clone() { IFormatter formatter = new BinaryFormatter(); using (stream = new MemoryStream()) { formatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return (Person)formatter.Deserialize(stream); } } }
One very important thing to notice is that we’ve had to decorate our class with a [Serializable] attribute, without it we get :
Type 'Person' is not marked as serializable.
It’s also somewhat annoying to have to add this code to every class, so it’s far more likely that we create a static cloning service like this :
public static class CloningService { public static T Clone<T>(this T 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(); using (stream = new MemoryStream()) { formatter.Serialize(stream, source); stream.Seek(0, SeekOrigin.Begin); return (T)formatter.Deserialize(stream); } } }
We still however have that issue of marking things as [Serializable].
JSON Serializer Cloning
This method is… Well.. a little rough but I’ve honestly found it works the best. You’ll first need to install the Newtonsoft.Json nuget package.
Install-Package Newtonsoft.Json
Then you can modify our cloning service like so :
public static class CloningService { public static T Clone<T>(this T source) { // Don't serialize a null object, simply return the default for that object if (Object.ReferenceEquals(source, null)) { return default(T); } var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace }; var serializeSettings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, serializeSettings), deserializeSettings); } }
And can I just say, everytime I’ve had to do this I think “Converting this to JSON and back again really isn’t great….”. But it just works. It handles everything you throw at it and is pretty much faultless.
In my opinion, if you are looking for a sure fire, re-useable way to clone objects in your code. This is it.
Reference Loops When Cloning
Eagle eyed readers will notice that in the above JSON cloning service, we have a line to handle reference looping. This is a very very common occurence, especially in models that are used as part of a DataModel where the two classes will reference each other. No matter how you decide to clone objects, you will always have issues where objects reference each other and any attempt to clone just spirals into an endless loop.
So even though in the above code, we solve it using a setting, it’s worth pointing out that no matter which way you decide to clone objects, this is going to always be a problem.
What’s Your Solution?
Think this page is full of hacks (it is!), and have a much better way to clone objects? Feel free to drop a comment below.
Another way to clone an object – use Automapper. This way cloning could be manageble.
And yet another one – using Reflection https://weblog.west-wind.com/posts/2009/Aug/04/Simplistic-Object-Copying-in-NET
Wow I didn’t even think of Automapper. That’s actually a pretty great idea.
This article reads like my autobiographical journal of working with .Net for the past 16 years! Basically it is the exact some progression of learning and working with cloning before there was stackoverflow. As I started reading my brain was screaming: “Just use the Json Serializer! Just use the Json Serializer!”, lol. What I appreciate is confirmation of something I thought was clever but hacky…!
great, thanks
BinaryFormatter is now deprecated as of .NET5
Using AutoMapper is discouraged by the author unfortunately, refer https://github.com/AutoMapper/AutoMapper/issues/340 and https://github.com/AutoMapper/AutoMapper/issues/405.
For binary serialization/deserialization try MessagePack instead, https://github.com/neuecc/MessagePack-CSharp