Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

Shallow Copy vs Deep Copy ใน C# ต่างกันยังไง

Shallow Copy vs Deep Copy ใน C#

การ Copy Object ใน C# มี 2 แบบหลัก ที่แตกต่างกันในการจัดการ Reference Type ภายใน Object

Shallow Copy (คัดลอกตื้น)

  • คัดลอก Value Type Fields (int, bool, struct, etc.) แบบ Value
  • คัดลอก Reference Type Fields (class, array, string) แบบ Reference (Address)
  • Object เดิมและ Copy แชร์ Reference Type Fields เดียวกัน
  • แก้ไข Reference Field ใน Copy → ส่งผลต่อ Original ด้วย

Deep Copy (คัดลอกลึก)

  • คัดลอก ทุกอย่าง รวมถึง Reference Type Fields (สร้าง Instance ใหม่)
  • Object เดิมและ Copy อิสระจากกันสมบูรณ์
  • แก้ไข Copy ไม่กระทบ Original

ตัวอย่างเปรียบเทียบ


using System;

class Program
{
    static void Main()
    {
        Console.WriteLine("=== Shallow Copy ===");
        ShallowCopyDemo();
        
        Console.WriteLine("\n=== Deep Copy ===");
        DeepCopyDemo();
    }
    
    static void ShallowCopyDemo()
    {
        // Class ที่มี Reference Type Field (List)
        var original = new Person
        {
            Name = "John",
            Age = 30,
            Addresses = new List { "123 Main St", "456 Oak Ave" }
        };
        
        // Shallow Copy: MemberwiseClone (Protected) หรือ Manual Copy
        var shallow = original.ShallowCopy();
        
        Console.WriteLine($"Original Name: {original.Name}");  // John
        Console.WriteLine($"Shallow Name: {shallow.Name}");    // John
        
        // เปลี่ยน Value Type (ไม่กระทบ Original)
        shallow.Age = 25;
        Console.WriteLine($"\nAfter shallow.Age = 25:");
        Console.WriteLine($"Original Age: {original.Age}");  // 30
        Console.WriteLine($"Shallow Age: {shallow.Age}");    // 25
        
        // เปลี่ยน Reference Type (กระทบ Original!)
        shallow.Addresses.Add("789 Pine Rd");
        Console.WriteLine($"\nAfter shallow.Addresses.Add():");
        Console.WriteLine($"Original Addresses: {string.Join(", ", original.Addresses)}");  // 3 items!
        Console.WriteLine($"Shallow Addresses: {string.Join(", ", shallow.Addresses)}");    // 3 items!
        Console.WriteLine($"Same List Reference? {object.ReferenceEquals(original.Addresses, shallow.Addresses)}");  // True
    }
    
    static void DeepCopyDemo()
    {
        var original = new Person
        {
            Name = "Jane",
            Age = 28,
            Addresses = new List { "100 First St" }
        };
        
        // Deep Copy: Manual / Serialization / Library
        var deep = original.DeepCopy();
        
        Console.WriteLine($"Original Name: {original.Name}");  // Jane
        Console.WriteLine($"Deep Name: {deep.Name}");          // Jane
        
        // เปลี่ยน Value Type
        deep.Age = 35;
        Console.WriteLine($"\nAfter deep.Age = 35:");
        Console.WriteLine($"Original Age: {original.Age}");  // 28
        Console.WriteLine($"Deep Age: {deep.Age}");          // 35
        
        // เปลี่ยน Reference Type (ไม่กระทบ Original!)
        deep.Addresses.Add("200 Second Ave");
        Console.WriteLine($"\nAfter deep.Addresses.Add():");
        Console.WriteLine($"Original Addresses: {string.Join(", ", original.Addresses)}");  // 1 item
        Console.WriteLine($"Deep Addresses: {string.Join(", ", deep.Addresses)}");          // 2 items
        Console.WriteLine($"Same List Reference? {object.ReferenceEquals(original.Addresses, deep.Addresses)}");  // False
    }
}

// Class สำหรับ Demo
class Person
{
    public string Name { get; set; }      // Reference Type (immutable แต่เป็น reference)
    public int Age { get; set; }          // Value Type
    public List Addresses { get; set; }  // Reference Type (Mutable)
    
    // Shallow Copy: ใช้ MemberwiseClone (Protected) ต้อง expose ผ่าน Method
    public Person ShallowCopy()
    {
        return (Person)this.MemberwiseClone();
    }
    
    // Deep Copy: Manual Implementation
    public Person DeepCopy()
    {
        return new Person
        {
            Name = this.Name,  // string immutable - copy reference OK
            Age = this.Age,
            Addresses = new List(this.Addresses)  // สร้าง List ใหม่ Copy ค่า
        };
    }
}

ผลลัพธ์:

=== Shallow Copy ===
Original Name: John
Shallow Name: John

After shallow.Age = 25:
Original Age: 30
Shallow Age: 25

After shallow.Addresses.Add():
Original Addresses: 123 Main St, 456 Oak Ave, 789 Pine Rd
Shallow Addresses: 123 Main St, 456 Oak Ave, 789 Pine Rd
Same List Reference? True

=== Deep Copy ===
Original Name: Jane
Deep Name: Jane

After deep.Age = 35:
Original Age: 28
Deep Age: 35

After deep.Addresses.Add():
Original Addresses: 100 First St
Deep Addresses: 100 First St, 200 Second Ave
Same List Reference? False

วิธีทำ Deep Copy ทางเลือกต่างๆ

1. Manual Copy (Constructor/Method) - เร็วที่สุด ควบคุมได้


public class Person
{
    public Person DeepCopy() => new Person
    {
        Name = this.Name,
        Age = this.Age,
        Addresses = new List(this.Addresses),
        // Nested Object ต้อง Deep Copy ต่อ
        Profile = this.Profile?.DeepCopy()
    };
}

2. JSON Serialization (ง่าย ช้า)


using System.Text.Json;

public T DeepCopyJson(T obj)
{
    var json = JsonSerializer.Serialize(obj);
    return JsonSerializer.Deserialize(json);
}

3. Binary Serialization (Legacy - ต้อง [Serializable])


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

public T DeepCopyBinary(T obj) where T : class
{
    using var ms = new MemoryStream();
    var formatter = new BinaryFormatter();
    formatter.Serialize(ms, obj);
    ms.Position = 0;
    return (T)formatter.Deserialize(ms);
}

4. AutoMapper / Mapster (Library)


// AutoMapper
var config = new MapperConfiguration(cfg => cfg.CreateMap());
var mapper = config.CreateMapper();
var deepCopy = mapper.Map(original);

// Mapster (เร็วกว่า)
var deepCopy = original.Adapt();

5. ICloneable Interface (Standard Pattern)


public class Person : ICloneable
{
    public string Name { get; set; }
    public int Age { get; set; }
    public List Addresses { get; set; }
    
    public object Clone()
    {
        return new Person
        {
            Name = this.Name,
            Age = this.Age,
            Addresses = new List(this.Addresses)
        };
    }
}

// Usage
var copy = (Person)original.Clone();

ตารางเปรียบเทียบวิธี Deep Copy

วิธีSpeedComplexityRemarks
Manual⚡ FastestHighControl ทุกอย่าง เหมาะ Performance Critical
JSON Serialize🐢 SlowLowง่าย รองรับ Complex Graph ต้อง [JsonInclude] private fields
Binary Formatter🐢 SlowLowLegacy, Security Risk, Obsolete
Mapster⚡ FastLowLibrary แนะนำ เร็ว ใช้ Expression Tree
AutoMapper🐢 MediumLowFeature ริช แต่ช้ากว่า Mapster
MemberwiseClone + Manual Fix⚡ FastMediumShallow แล้ว Fix Reference Fields เอง

Struct = Value Type = Auto Deep Copy


struct Point
{
    public int X, Y;
    public List Data;  // Reference Type ใน Struct!
}

var p1 = new Point { X = 1, Y = 2, Data = new List { 1, 2 } };
var p2 = p1;  // Value Type Copy - ทุก Field ถูก Copy

p2.X = 10;
p2.Data.Add(3);

Console.WriteLine(p1.X);  // 1 (ไม่เปลี่ยน)
Console.WriteLine(p1.Data.Count);  // 3! ⚠️ Reference Type ใน Struct ยัง Share อยู่
⚠️ Struct ที่มี Reference Type Field ยังคงเป็น Shallow Copy สำหรับ Field นั้น!
Struct Copy = Bitwise Copy ของ Fields ทั้งหมด Reference Field ยังชี้ Object เดียวกัน

สรุป

  • Shallow Copy: Copy Value Types, Share Reference Types (MemberwiseClone)
  • Deep Copy: Copy ทุกอย่าง สร้าง Instance ใหม่ทั้งหมด
  • Default Assignment (=) สำหรับ Class = Reference Copy (ชี้ Object เดียวกัน)
  • Default Assignment สำหรับ Struct = Value Copy (แต่ Ref Fields ยัง Share)
  • เลือกวิธี Deep Copy ตาม: Performance, Complexity, Library Dependency
  • Immutable Types (string, record, readonly struct) - Copy Reference ปลอดภัย