จากบทความตอนที่แล้ว ความแตกต่างระหว่าง 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 (Binary Formatter)
Update: 2022 สำหรับวิธีการนี้ ถ้าเป็น .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.



