Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Shallow and Deep Cloning in C#

Tech 1

The MemberwiseClone method generates a shallow copy by creating a new instance and copying all non-static fields from the original object. For value types, a bitwise copy occurs; for reference types, the reference itself is copied, not the referenced object. Consequently, both the original and cloned objects point to the same memory location for reference-type fields.

To achieve deep cloning, one must traverse the object graph that may contain circular references, which is complex. Fortunately, .NET's serialization capabilities simplify deep cloning. The process involves serializing the object into a memory stream, thereby preserving the state of the object and all referenced objects. The .NET serialization mechanism handles circular references automatically. Then, the serialized data is deserialized into a new object, completing the deep clone operation. This technique is essential in prototype pattern implementations.

Below is an example demonstrating these concepts:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace CloneDemo
{
    [Serializable]
    class DemoClass
    {
        public int i = 0;
        public int[] iArr = { 1, 2, 3 };

        public DemoClass Clone1() // Shallow clone
        {
            return this.MemberwiseClone() as DemoClass;
        }

        public DemoClass Clone2() // Deep clone
        {
            using (MemoryStream stream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, this);
                stream.Position = 0;
                return formatter.Deserialize(stream) as DemoClass;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            DemoClass a = new DemoClass();
            a.i = 10;
            a.iArr = new int[] { 8, 9, 10 };
            DemoClass b = a.Clone1();
            DemoClass c = a.Clone2();

            // Modifying a.iArr[0] affects b.iArr[0] but not c.iArr[0]
            a.iArr[0] = 88;

            Console.WriteLine("MemberwiseClone");
            Console.WriteLine(b.i);
            foreach (var item in b.iArr)
            {
                Console.WriteLine(item);
            }

            Console.WriteLine("Clone2");
            Console.WriteLine(c.i);
            foreach (var item in c.iArr)
            {
                Console.WriteLine(item);
            }

            Console.ReadLine();
        }
    }
}

Another illustration uses arrays, which are reference types in C#. Here's how shalow copying works:

using System;

class ShallowCopy : ICloneable
{
    public int[] v = {1, 2, 3};

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

    public void Display()
    {
        foreach (int i in v)
            Console.Write(i + ", ");
        Console.WriteLine();
    }
}

class Client
{
    public static void Main()
    {
        ShallowCopy sc1 = new ShallowCopy();
        ShallowCopy sc2 = (ShallowCopy)sc1.Clone();
        sc1.v[0] = 9;

        sc1.Display();
        sc2.Display();
    }
}

In this case, the shallow copy duplicates the object but not its array field v. Both sc1 and sc2 refer to the same array, so modifying sc1.v[0] also changes sc2.v[0].

For a deep copy implementation:

using System;

class DeepCopy : ICloneable
{
    public int[] v = {1, 2, 3};

    public DeepCopy() {}

    private DeepCopy(int[] v)
    {
        this.v = (int[])v.Clone();
    }

    public object Clone()
    {
        return new DeepCopy(this.v);
    }

    public void Display()
    {
        foreach (int i in v)
            Console.Write(i + ", ");
        Console.WriteLine();
    }
}

class Client
{
    public static void Main()
    {
        DeepCopy dc1 = new DeepCopy();
        DeepCopy dc2 = (DeepCopy)dc1.Clone();
        dc1.v[0] = 9;

        dc1.Display();
        dc2.Display();
    }
}

Here, the clone method creates a new DeepCopy instance with a cloned array, ensuring dc1 and dc2 have independent data.

A utility class for deep cloning can also be created:

public static class ObjectCopier
{
    public static T Clone<T>(T source)
    {
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", nameof(source));
        }

        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.