Create a deep clone of an object instance

Within this article I want to show you two ways to create a deep clone of an object instance.

 
Flat clone vs. deep clone

If you want to create a clone of an object instance, in most cases of a data class, you have to decide whether you need a flat or a deep clone. The needed clone mechanism depends on your use cases. In most use cases a deep clone is needed, otherwise reference type members may lead to unexpected behaviours because they will access the same elements in the origin and the cloned data class.

 
Flat clone

It is very ease to create a flat clone. The object class contains the method MemberwiseClone which returns a flat copy of the given object. The following source code shows an example how to create a flat clone.

var clone = origin.MemberwiseClone();

 
Deep clone

To create a deep clone is not that trivial like to create a flat one. There existing several common approaches to implement such a clone function and you will find a lot of open source projects or code snippets which you can use. Most of these solutions are using serialization or reflection to clone an object. Both approaches allow implement functions or extension methods which may be used to clone nearly all possible data classes. But both approaches also have some disadvantages. Serialization is slow and not all objects support serialization. Reflection is also slow and may result in errors if you have circular references.

Within this article I will show you two possible implementations. One common approach based on serialization and one approach which needs more work but will allow you to implement a fast clone mechanism.

 
Data classes

For our example I want to use two data classes. One class is used within the other class as a sub element. The following source code shows the data classes and the according interfaces.

public interface ISubElement
{
    double DoubleValue { get; set; }
}

public interface IElement
{
    int IntValue { get; set; }
    string StringValue { get; set; }
    List<ISubElement> SubElements { get; set; }
}

public class SubElement : ISubElement
{
    public SubElement()
    {
    }

    public double DoubleValue { get; set; }
}

public class Element : IElement
{
    public Element()
    {
    }

    public int IntValue { get; set; }
    public string StringValue { get; set; }
    public List<ISubElement> SubElements { get; set; }
}

 
Serialization

To clone an object you may use the serialization features of the .NET Framework. The following source code shows an extension method to create a deep clone of an object.

public static T DeepClone<T>(this T source)
{
    using (var memoryStream = new MemoryStream())
    {
        var binaryFormatter = new BinaryFormatter();

        binaryFormatter.Serialize(memoryStream, source);
        memoryStream.Position = 0;

        return (T)binaryFormatter.Deserialize(memoryStream);
    }
}

 
To use this function you have to add the [Serialization] attribute to the data classes and you may use the ISerializable interface and implement the clone function in each data class. The following source code shows the adapted data classes.

public interface ISubElement : ICloneable
{
    double DoubleValue { get; set; }
}

public interface IElement : ICloneable
{
    int IntValue { get; set; }
    string StringValue { get; set; }
    List<ISubElement> SubElements { get; set; }
}

[Serializable]
public class SubElement : ISubElement
{
    public SubElement()
    {
    }

    public object Clone()
    {
        return this.DeepClone();
    }

    public double DoubleValue { get; set; }
}

[Serializable]
public class Element : IElement, ICloneable
{
    public Element()
    {
    }

    public object Clone()
    {
        return this.DeepClone();
    }

    public int IntValue { get; set; }
    public string StringValue { get; set; }
    public List<ISubElement> SubElements { get; set; }
}

 
Now it is possible to use the clone function of the data classes to create a deep clone. The following console application shows an according example.

static void Main(string[] args)
{
    var myElement = new Element();

    myElement.IntValue = 10;
    myElement.StringValue = "foo";
    myElement.SubElements = new List<ISubElement>()
    {
        new SubElement() { DoubleValue = 15.5 },
        new SubElement() { DoubleValue = 21.3 }
    };

    //clone
    var myClone = (Element)myElement.Clone();

    //show in console
    Console.WriteLine("IntValue: " + myClone.IntValue.ToString());
    Console.WriteLine("StringValue: " + myClone.StringValue);
    Console.WriteLine("SubElement[0]: " + myClone.SubElements[0].DoubleValue.ToString());
    Console.WriteLine("SubElement[1]: " + myClone.SubElements[1].DoubleValue.ToString());
    Console.ReadKey();
}

 
Recursive cloning

As described the common cloning mechanisms have some disadvantages. Therefore I want to show you another approach to implement your own clone mechanism. This solution will allow you to create your own clone implementation adapted to the special needs of each of you data classes. This will allow you to implement a very efficient clone function but with the costs of a higher development effort.

The idea is to implement a clone function for each data class which creates a copy of all class members and if the member is a sub class it calls the clone function of this sub class. Therefore a recursive cloning will be done to create the deep clone.

The following source code shows the clone function of the SubElement class. This function is very easy to implement because this class contains only one member which is a normal data type. Furthermore I have added a copy constructor because it will make it easier to create a copy of the class.

public class SubElement : ISubElement
{
    public SubElement()
    {
    }

    public SubElement(
        double doubleValue)
    {
        DoubleValue = doubleValue;
    }

    public object Clone()
    {
        return new SubElement(DoubleValue);
    }

    public double DoubleValue { get; set; }
}

 
To implement the clone function of the Element class is more difficult. This class contains reference types, like the List<>, and it contains sub classes. Therefore a copy of each reference type and the recursive clone of the sub class must be created.

public class Element : IElement
{
    public Element()
    {
    }

    public Element(
        int intValue,
        string stringValue,
        List<ISubElement> subElements)
    {
        IntValue = intValue;
        StringValue = stringValue;
        SubElements = subElements;
    }

    public object Clone()
    {
        List<ISubElement> clonedSubElements = null;

        if (SubElements != null)
        {
            clonedSubElements = new List<ISubElement>();

            foreach (ISubElement subElement in SubElements)
            {
                clonedSubElements.Add((ISubElement)subElement.Clone());
            }
        }

        return new Element(
            IntValue,
            StringValue,
            clonedSubElements);
    }

    public int IntValue { get; set; }
    public string StringValue { get; set; }
    public List<ISubElement> SubElements { get; set; }
}

 
To test this new clone functions you may use the same console application shown above for the serialization solution.

 
Summary

If you want to copy classes, e.g. data classes, you have to think carefully whether you need flat or deep clones. To create deep clones you may use one of the common approaches based on serialization or reflection. In case the disadvantages of these common approaches do not fit with the needs of your application you may implement your own clone functions. As shown in this article you can implement a class specific clone function which uses recursive copy mechanisms to create a deep clone. This will allow you to implement a very fast cloning method but with the disadvantage of higher implementation effort.

Advertisements
Dieser Beitrag wurde unter .NET, C# abgelegt und mit , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s