Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding Deep Copy and Shallow Copy in C#

Tech Jul 21 2

Copy Semantics Overview

Object copying is a fundamental concept in C# that frequently appears in technical discussions and interviews. All .NET types inherit from System.Object, which provides a protected method MemberwiseClone() that performs what is known as a shallow copy.

Shallow Copy

A shallow copy creates a new object, then copies all non-static value type fields and all reference type field references to the new object. The key characteristic is that reference type member are shared between the original and copied objects—they point to the same underlying objects in memory.

Consider a simple example with a reference type:

public class Dog
{
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        Dog original = new Dog { Name = "Max" };
        Dog shallow = original; // Shallow copy via reference assignment
        
        shallow.Name = "Buddy";
        
        Console.WriteLine($"Original: {original.Name}, Shallow: {shallow.Name}");
        // Output: Both show "Buddy" because they reference the same object
    }
}

With a reference type member, modifying the shared reference affects all references:

public class Pet
{
    public string Nickname { get; set; }
}

public class Owner
{
    public Pet Animal { get; set; }
    public int Age { get; set; }
}

class Program
{
    static void Main()
    {
        Owner ownerA = new Owner 
        { 
            Age = 30, 
            Animal = new Pet { Nickname = "Rex" } 
        };
        
        Owner ownerB = (Owner)ownerA.MemberwiseClone();
        
        ownerB.Animal.Nickname = "Spot";
        
        Console.WriteLine(ownerA.Animal.Nickname); // Prints "Spot"
    }
}

In this scenario, ownerB.Animal points to the same Pet instance as ownerA.Animal. Changing the nickname through either owner affects the shared object.

Deep Copy

A deep copy duplicates not only the value types but also creates entirely new copies of all reference type members. After a deep copy, the original and copy are completely independent—modifications to one do not affect the other.

The ICloneable interface provides a standard pattern for implementing copy functionality, though it can be implemented as either shallow or deep copy depending on requirements:

public interface ICloneable
{
    object Clone();
}

public class Person : ICloneable
{
    public string Name { get; set; }
    public Address HomeAddress { get; set; }
    public int Score { get; set; }
    
    public object Clone()
    {
        Person copy = new Person();
        copy.Name = Name;
        copy.Score = Score;
        copy.HomeAddress = new Address 
        { 
            Street = HomeAddress.Street, 
            City = HomeAddress.City 
        };
        return copy;
    }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
}

class Program
{
    static void Main()
    {
        Person original = new Person
        {
            Name = "Alice",
            Score = 100,
            HomeAddress = new Address { Street = "123 Main St", City = "Seattle" }
        };
        
        Person cloned = (Person)original.Clone();
        cloned.HomeAddress.City = "Portland";
        
        Console.WriteLine(original.HomeAddress.City); // Still "Seattle"
    }
}

Key Differences Illustrated

Aspect Shallow Copy Deep Copy
Value Types Duplicated Duplicated
Reference Types Shared reference New object created
Independence Dependent Independent

Implementation Approaches

Manual Property Assignment

The most straightforward approach—create a new instance and assign each property individually:

public class Item
{
    public string Label { get; set; }
    public decimal Price { get; set; }
    public CategoryInfo Category { get; set; }
}

public class CategoryInfo
{
    public string Name { get; set; }
}

public static Item Duplicate(Item source)
{
    return new Item
    {
        Label = source.Label,
        Price = source.Price,
        Category = new CategoryInfo { Name = source.Category.Name }
    };
}

Reflection-Based Cloning

Dynamic cloning using reflection works for most scenarios:

using System.Reflection;

public static T CloneViaReflection<T>(T source) where T : new()
{
    T result = new T();
    Type type = typeof(T);
    
    foreach (PropertyInfo prop in type.GetProperties())
    {
        if (prop.CanWrite)
        {
            object value = prop.GetValue(source);
            prop.SetValue(result, value);
        }
    }
    
    return result;
}

Note: This approach copies reference types by reference, which may not achieve true deep copying for nested reference types.

Serialization Techniques

JSON Serialization

Using Newtonsoft.Json (or System.Text.Json):

using Newtonsoft.Json;

public static T DeepClone<T>(T obj)
{
    string json = JsonConvert.SerializeObject(obj);
    return JsonConvert.DeserializeObject<T>(json);
}

Binary Serialization

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

public static T DeepClone<T>(T obj)
{
    using (MemoryStream stream = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, obj);
        stream.Position = 0;
        return (T)formatter.Deserialize(stream);
    }
}

XML Serialization

using System.Xml.Serialization;

public static T DeepClone<T>(T obj)
{
    using (MemoryStream stream = new MemoryStream())
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        serializer.Serialize(stream, obj);
        stream.Position = 0;
        return (T)serializer.Deserialize(stream);
    }
}

Design Considerations

Inheritence concerns: Implementing ICloneable on a base class that will be inherited creates an obligation for all derived types to also implement the interface properly. Failing to do so means inherited reference type members won't be deeply copied by subclasses.

Performance: Deep copying via serialization has overhead. For frequently copied objects with complex graphs, manual copying or specialized copy constructors may offer better performence.

Security: When using serialization for cloning, ensure the type is marked [Serializable] and consider whether custom serialization logic is needed to handle sensitive fields appropriately.

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.