Sunday, January 20, 2013

Cloning an object in C# (deep copy)

How can we clone an object in C#?

In C#, if we are inheriting the base class System.Object we can see there is a protected method named MemberwiseClone. This method provides a facility to create a copy of our current object. Below is the basic sample.

public class Person : System.Object
{
    public Person Clone()
    {
        return (Person)base.MemberwiseClone();
    }

    public string Name
    {
        get;
        set;
    }
}

This is a very good method as it provides a copy of the object we wanted to copy. But what if we want to copy an object ignoring the references on that object? This method cannot address our scenario since the method MemberwiseClone only provide us a shallow copy of an object.

For you to understand further, shallow copy only creates a pointer to the current object without creating an another pointer to an object referenced to it. Meaning, if there is a "byref" object property in that object, it will not create an another object of the object (it only reference it). Below are the codes extending the object above and will explain the shallow copy.

public class Person : System.Object
{
    public Person(string name, Pet pet)
    {
        this.Name = name;
        this.Pet = pet;
    }

    public Person Clone()
    {
        return (Person)base.MemberwiseClone();
    }

    public string Name
    {
        get;
        private set;
    }

    public Pet Pet
    {
        get;
        private set;
    }
}

public class Pet : System.Object
{
    public string Name
    {
        get;
        set;
    }

    public string Type

    {
        get;
        set;
    }
}

We created an additional class named Pet. Note it is a class (not struct) and is a "by-ref" object. We also add a new method named Clone but calling the base object MemberwiseClone method. See how the referencing happens below.

var michael = new Person("Michael", new Pet { Type = "Dog", Name = "Scooby" });
MessageBox.Show(michael.Pet.Name);

var cloned = michael.Clone();
cloned.Pet.Name = "Scrappy";
MessageBox.Show(string.Format("{0}={1}", michael.Pet.Name, cloned.Pet.Name));

What we did is we created a Person object named Michael and its Pet's name as Scooby. We then copy the object and change the Pet name to Scrappy. Notice that the MessageBox will display the same Pet name. This is the reason because the object was cloned in a shallow way. Any reference to the property object as long it is a "by-ref" object will be referenced to the cloned object.

Deep Cloning

A deep cloning is a technique of serializing/deserializing of an object into the computer's memory. All its doing is to create a copy of pointer in the heap (memory) - (an additional pointer) and will give you an ability to deserialize it as the same object in .NET. Below is the code how to do it (extending the class above).

public class Person : System.Object
{
    public Person(string name, Pet pet)
    {
        this.Name = name;
        this.Pet = pet;
    }

    public Person Clone()
    {
        return (Person)base.MemberwiseClone();
    }

    public Person DeepClone()
    {
        try
        {
            using (var stream = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(stream, this);
                stream.Position = 0;
                return (Person)formatter.Deserialize(stream);
            }
        }
        catch
        {
            throw;
        }
    }

    public string Name
    {
        get;
        private set;
    }

    public Pet Pet
    {
        get;
        private set;
    }
}

The new method created is DeepClone. With the help of MemoryStream and BinaryFormatter, we can do serialize an object into the memory and deserialize it as the same object in .NET. However, the difference are the properties bound to the object even it is a "by-ref" object will also be recreated. It has created a different pointer in our computer's memory.

So with the code's above calling the DeepClone method will address the issue.

Note: For us to participate in the Serialization, we need to put an [Serializable] attribute in all class participated in the operation. In our case, we need to place that attribute above Pet and Person class. Please visit Microsoft documentation for more information.

I am hoping you guys read the explanation as this is a very common question during in the interviews.

No comments:

Post a Comment

Place your comments and ideas