จากบทความตอนที่แล้ว ความแตกต่างระหว่าง 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.