[C#] Shallow Copy กับ Deep Copy

จากบทความตอนที่แล้ว ความแตกต่างระหว่าง Shallow copy กับ Deep copy คราวนี้เรามาลองดูการใช้งานจริงในภาษา C# บ้างนะครับ สำหรับวิธีการ Copy Object ใน C# มีวิธีที่สามารถทำได้ทั้งหมด ดังนี้

Implement Interface ICloneable

  • เป็น Interface ที่กำหนดกรอบคร่าวสำหรับการ Copy หรือ Clone ส่วนจะ Implement เป็น Shallow Copy หรือ Deep Copy แล้วแต่คนเขียน Code เลยครับ
  • ข้อควรระวัง ไม่เป็น Type-Safe เพราะ Method Clone ของ Interface ICloneable มีการ Return ค่าเป็น Object

MemberwiseClone

  • ปรับปรุงจาก ICloneable โดยทำให้เป็น Type-Safe ครับ
  • ตัวอย่าง Code ครับ
public class Security : ICloneable
{
    public string Name;
    object ICloneable.Clone()
    {
        return this.Clone();
    }
    public Security Clone()
    {
        return (Security )this.MemberwiseClone();
    }
}
  • ส่วนการเรียกใช้งานครับ
Security BBL = new Security();
BBL.Name = "Bangkokbank";
Security BBLClone = Security.Clone();

Reflection

  • ใช้ Activator.CreateInstance
  • ช้ากว่าแบบแรกเลยครับ
  • ทำ Deep Clone ได้
  • ไม่สามารถใช้กับ Partial Trust Environments (มองง่ายๆว่าพื้นที่ Code ของเราไม่สามารถใช้สิทธิ์ใน Resource ตางๆ ได้เต็มที่ครับ ติด Permission ต่างๆ เช่น Web Service บน Share Host หรือ เครื่อง Client ที่อยู่บน Active Directory)ได้
  • ตัวอย่างของ Code ครับ
public static class ObjectExtension
{
    public static object CloneObject(this object objSource)
    {
        //ดึง Type ของ Object ออกมา และสร้าง Object ใหม่ โดยใช้ Activator.CreateInstance
        Type typeSource = objSource.GetType();
        object objTarget = Activator.CreateInstance(typeSource);
 
        //List Property ของ Object ต้นทางมาให้หมด
        PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
 
        //Loop ยัดค่าลงไป
        foreach (PropertyInfo property in propertyInfo)
        {
            //ตรวจสอบก่อนว่า Property สามารถเขียนได้ หรือไม่
            if (property.CanWrite)
            {
                //ตรวจสอบ Type ถ้าเป็น Enum หรือ String ก็ยัดข้อมูลงไปได้
                if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String)))
                {
                    property.SetValue(objTarget, property.GetValue(objSource, null), null);
                }
                //ถ้าไม่ใช้เงื่อนไขข้างต้น ต้อง Recursive เพราะเป็น Property ที่มีเงื่อนไขซับซ้อน
                else
                {
                    object objPropertyValue = property.GetValue(objSource, null);
                    if (objPropertyValue == null)
                    {
                        property.SetValue(objTarget, null, null);
                    }
                    else
                    {
                        property.SetValue(objTarget, objPropertyValue.CloneObject(), null);
                    }
                }
            }
        }
        return objTarget;
    }
}

Serialization

สำหรับวิธีการนี้ ถ้าเป็น .NET Core 5 ไม่สามารถใช้งานได้แล้วนะครับ และถูกเอาออกถาวรใน .NET9
Ref: Deserialization risks in use of BinaryFormatter and related types - .NET | Microsoft Learn

  • สำหรับคนที่งงๆ เรื่อง Serialization สามารถดู Overview ได้ที่นี่ครับ
  • ทำ Deep Copy ได้
  • Object ที่สามารถทำ Deep Copy ได้ ต้องมี Serializable กำกับเสมอ
  • ช้ากว่าแบบอื่นๆเลยครับ แต่ไม่ต้องกังวลเวลาเราไป Add หรือ Remove Property ชนิดใหม่ เพราะเป็นการ Copy จากหน่วยความจำตรงๆครับ
  • ตัวอย่างของ Code โดยผมได้ทำเป็น Extension Method ครับ
public static class ObjectExtension
{
    public static T DeepCopy<T>(this T a)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, a);
            stream.Position = 0;
            return (T)formatter.Deserialize(stream);
        }
    }
}

Intermediate Language (IL)

  • ก่อนอื่นเลยเจ้า Intermediate Language (IL) คือ ภาษาที่ .Net Framework แปลงจาก C#, VB.Net และ F# เป็นต้น เราอาจจะมองคล้ายกับ Java bytecode ของฝั่ง Java ครับ
  • ทำ Shallow Copy กับ Deep Copy ได้
  • อันนี้ Concept หลัก คือ การใช้ Method DynamicMethod เข้ามาช่วย
  • เดี๋ยวผมเข้าใจ แล้วจะมาอัพเดทนะครับ เอาตัวอย่างไปก่อน ฮ่าๆ

อ่านกันมาจนจบและ จะเห็นว่ามีหลากหลายวิธีนะครับ ในการ Copy Object ซึ่งใน C# รุ่นใหม่ตั้งแต่ 3.5 เป็นต้นไปมั้ง สามารถทำเป็น Extension Method (เป็นการเพิ่มความสามารถใช้กับ Object นั้น เวลาใครเอาไปใช้ Method พวกนี้ก็จะติดไปด้วยครับ) อ๋อและก็ส่วนตัวผมใช้ แบบ MemberwiseClone กับ Serialization(ถึงแม้จะช้า แต่มันก็ต่างกันไม่มากครับ ละก็ไม่เจอปัญหา Partial Trust Environments ด้วยครับ โดยทำเป็น Extension Method แหละง่ายดี ^___^


Discover more from naiwaen@DebuggingSoft

Subscribe to get the latest posts sent to your email.