A solution to Deep Cloning in C#

ยท

7 min read

A solution to Deep Cloning in C#

First of all, cloning an object in C# usually mean to create a new instance of the same type with all data copied from the original instance over to the new instance.

There are a few ways to clone objects in C#. You can for example, implement ICloneable which comes with the .NET BCL, you can use serialization and deserialization libraries, you can implement your own interface if you only need this inside a codebase you own, or you can use the reflection capabilities of the .NET runtime, etc.

But there are two ways to clone objects and sometimes it's not obvious which is being used.

Shallow cloning usually means that the new object will use the assignment operator (=) to copy values from the original object to the new, cloned object. That means reference types will still point to the same instances as the original properties or fields. Shallow clones are thus easy to implement and can even be expressed very easily like this:

var clone = original as { };

The above syntax is available in C# 9, but if you can't use that, you can use Object.MemberwiseClone to perform a shallow clone like this:

public class Person
{
    public int Age;
    public string Name;
    public IdInfo IdInfo;

    public Person ShallowClone()
    {
       return (Person) this.MemberwiseClone();
    }
}

You can also make use of MemberwiseClone to implement a deep clone like this:

public class Person
{
    // ...

    public Person DeepClone()
    {
       // first a shallow copy to take care of all value types:
       Person other = (Person) this.MemberwiseClone();
       // then a manual deep clone for reference types:
       other.IdInfo = new IdInfo(IdInfo.IdNumber);
       // notice we've skipped immutable value types like string and int;
       // int's are copied on assignment and string is immutable so it's
       // ok if we share the same reference between objects.
       return other;
    }
}

If you search around the Internet, you will find many solutions to the problem of cloning and deep cloning especially, but usually you will find these options:

Option 1: Serialize and Deserialize the object to some other format, usually binary, because it's faster than text. This is, frankly, the worst option in my opinion. It's the slowest method to execute (if you need to care about execution speed) and it's prone to runtime errors because it uses reflection which is a runtime feature. For example, it might throw an exception or even crash on circular references.

To me this option feels more like a hack. The good thing about it is that, while it might not be the fastest to execute, it's the fastest to implement. For example, if you use a library like AutoMapper, you can deeply clone an object like this:

// AutoMapper configuration using the default mapping implementation:
Mapper.Initialize(cfg => cfg.CreateMap<Partner, Partner>());

// then in your code, create the deep clone using AutoMapper like this:
var deeplyClonedPartner = Mapper.Map<Partner>(originalPartner);

Most people probably use this option until they find a problem with it, if they even find one at all.

Option 2: Implement ICloneable but it's not meant to be implemented in library code, because there's no clear guarantee on which type of cloning you're doing, so you have to assume it's one or the other or test before you use it.

You can implement ICloneable and use Object.MemberwiseClone() to perform a shallow clone or a deep clone, like demonstrated above.

One thing I don't like about ICloneable, is that you need to use type casting with the resulting type everytime you call Clone, because the interface returns a weakly typed object. I'm not sure why ICloneable exists in the BCL... ๐Ÿค”

Option 3: Use a clone... sorry, copy constructor, like is more prevalent in C++, but that's not idiomatic to C#. C++'s implicit copy constructor is similar in behavior and implementation to C#'s Object.MemberwiseClone.

With copy constructors, you have to create one for each nested object, for recursive cloning to work, and there's also no way to know for sure which type of cloning you're doing without looking at the code.


By now you probably realize that deep cloning of objects is not so simple, and at least not simple enough to have it implemented automatically by the language or framework in a type safe manner. Even if some sort of cloning is implemented by default by the language or runtime, like with C++'s copy constructors, or JavaScript's spread operator, most implementations do a simple assignment copy and probably only for public properties. That is OK for simple value types that are cheap to clone and are actually cloned on assignment, but for reference types, this just copies the same references to the same objects. That's not a deep clone, it's a shallow clone, an alias.

Option 4: The last option and my preferred one: Write your own! ๐Ÿ˜

Writing your own implementation I think is the best option, because you can make it very clear about which type of cloning you're doing. A simple interface for an object of a generic type T, that can produce clones of itself, would look like this:

public interface IDeepCloneable<T>
{
    T DeepClone();
}

You can go a bit further and say:

/// <summary>
/// <a href="https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern">CRTP</a>-based
/// interface to implement for objects that can create deep clones of themselves,
/// but can be abused if TSelf is not specified as the same type as the
/// implementing class.
/// </summary>
/// <typeparam name="TSelf"></typeparam>
public interface IDeepCloneable<TSelf> where TSelf : IDeepCloneable<TSelf>
{
    public TSelf DeepClone();
}

I was looking above for a way to express a type parameter T where T : Self, or even to use a non-generic return type like public Self DeepClone(), but there is no concept of a Self reference in C# (yet), so what I actually use now is the above interface, which comes closest to the perfect solution, in my opinion, because it goes a bit further by restricting the returned type to also implement IDeepCloneable. The caveat is that implementers might abuse this interface and implement it with a type parameter other than the type that is implementing it.

Here's an example of a correct implementation:

public class Partner : IDeepCloneable<Partner>
{
    public int Id { get; init; }
    public string Name { get; init; }
    public string FiscalCode { get; init; }
    public DeepCloneableObject SomeProperty { get; init; }

    public Partner DeepClone()
    {
        return new Partner
        {
            Id = Id,
            // you don't need to clone strings,
            // because they are immutable reference types, but if you really wanted to clone
            // a string, you can use it's copy constructor `Name = new string(Name)`.
            Name = Name,
            FiscalCode = new string(FiscalCode),
            // you can also reuse the implementation of IDeepCloneable:
            SomeProperty = SomeProperty.DeepClone()
        };
    }
}

Here's an example of a bad implementation, like I was referring to above:

public class Partner : IDeepCloneable<SomeOtherType>
{
    public int Id { get; init; }
    public string Name { get; init; }
    public string FiscalCode { get; init; }

    public SomeOtherType DeepClone()
    {
        // not returning a Partner anymore:
        return new SomeOtherType
        {
            // ...
        };
    }
}

I like this approach because it's explicit, it's flexible, it's statically checked at compile time but most importantly, it holds a clear contract and guarantees it runs without errors. And if a class changes, you only need to change its implementation of IDeepCloneable in one place. Actually, if only a property's type changes and if that type is an immutable reference type like string or a mutable reference type that also implements IDeepCloneable, you don't need to update anything.

That's it. Thanks for reading this post! Hope your liked it.

If you have an opinion about which method is best or an idea about other ways to perform deep cloning, please leave a comment.

Bonus Extension Method

This extension method allows you to deeply clone collections (like Lists, etc.), assuming the items themselves also implement IDeepCloneable:

public static class DeepCloneExtensions
{
    /// <summary>
    /// Produces another list with the same objects deeply cloned using
    /// their implementation of <see cref="IDeepCloneable{TSelf}"/>.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static IEnumerable<T> DeepClone<T>(this IEnumerable<T> collection)
            where T : IDeepCloneable<T>
    {
        return collection.Select(item => item.DeepClone()).ToList();
    }
}

// now you can create a deep clone of List<T> like this:
var deeplyClonedList = myList.DeepClone();
ย