Interface ICloneable ใน C#
ICloneable ให้วิธีมาตรฐานในการสร้าง Copy (Clone) ของ Object มีเมธอดเดียวคือ Clone()
Definition
public interface ICloneable
{
object Clone();
}
Shallow Copy vs Deep Copy
| ประเภท | คำอธิบาย | ICloneable Default |
|---|---|---|
| Shallow Copy | Copy ค่า Field ทั้งหมด: Value Types คัดลอกค่า, Reference Types คัดลอก Reference (ชี้ Object เดียวกัน) | MemberwiseClone() |
| Deep Copy | Copy Object ทั้งหมดรวมถึง Object ที่อ้างอิง (สร้าง Instance ใหม่ทั้งหมด) | ต้อง Implement เอง |
ตัวอย่าง: Shallow Copy (Default Behavior)
using System;
class Person : ICloneable
{
public string Name; // Reference Type (Immutable แต่ Reference)
public int Age; // Value Type
public Address Address; // Reference Type (Mutable)
public Person(string name, int age, Address addr)
{
Name = name; Age = age; Address = addr;
}
public object Clone()
{
// Shallow Copy: MemberwiseClone
return this.MemberwiseClone();
}
public override string ToString() => $"{Name}, {Age}, Addr: {Address?.City}";
}
class Address
{
public string City;
public Address(string city) => City = city;
}
class Program
{
static void Main()
{
Person p1 = new Person("Tom", 25, new Address("Bangkok"));
Person p2 = (Person)p1.Clone(); // Shallow Copy
p2.Name = "Jerry"; // String Immutable - OK (New String)
p2.Age = 30; // Value Type - OK (Independent)
p2.Address.City = "Chiang Mai"; // Reference Type - SHARED! p1.Address เปลี่ยนด้วย
Console.WriteLine($"p1: {p1}"); // Tom, 25, Addr: Chiang Mai (!)
Console.WriteLine($"p2: {p2}"); // Jerry, 30, Addr: Chiang Mai
}
}
ตัวอย่าง: Deep Copy Implementation
public object Clone()
{
// Deep Copy: สร้าง Object ใหม่ทั้งหมด
return new Person(
this.Name, // String - Immutable, Share ได้
this.Age, // Value Type - Copy
new Address(this.Address.City) // Reference Type - New Instance
);
}
Alternative: Copy Constructor (แนะนำกว่า ICloneable)
class Person
{
public Person(Person other) // Copy Constructor
{
Name = other.Name;
Age = other.Age;
Address = new Address(other.Address.City); // Deep Copy
}
}
// ใช้งาน
Person p2 = new Person(p1); // ชัดเจนกว่า (Person)p1.Clone()
⚠️ ปัญหาของ ICloneable
- Return Type เป็น
object→ ต้อง Cast - ไม่ระบุว่า Shallow หรือ Deep → Ambiguous
- Framework Design Guidelines: หลีกเลี่ยง ICloneable ใน Public API
- แทนที่: ใช้ Copy Constructor, Factory Method, หรือ Record Types (C# 9+)
Record Types (C# 9+) - Built-in Cloning
public record Person(string Name, int Age, Address Address)
{
// with-expression สำหรับ Non-destructive Mutation (Deep Copy สำหรับ Reference Types ต้องจัดการเอง)
public Person WithNewAddress(string city) => this with { Address = new Address(city) };
}