深度Clone 一个快速,肮脏的方法(Deep Clone of a business object: the quick and dirty way)...

本文介绍如何在WPF、WCF和Silverlight环境中快速实现对象的深克隆,包括使用BinaryFormatter和DataContractSerializer的方法,以及在Silverlight中处理DataContract和DataMember属性的限制与解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文出处:http://www.primordialcode.com/index.php/2008/10/18/deep-clone-business-object-quick-dirty/

I was implementing the IEditableObject interface for some entity classes in my current project and I needed a quick and dirty way to do the deep clone of an object, since my entity classes are shared between WCF, WPF and Silverlight (yes I’m one of the guys so crazy to build multi-target applications) I needed a method that could work in all the environments.

In WPF and WCF you can rely on the binary formatter and the serialization (entity classes have to be marked with the Serializable attribute), and use code like this:

   1: public static partial class Helpers
   2: {
   3:     public static T DeepClone<T>(this T obj)
   4:     {
   5:         T cloned = default(T);
   6:         var serializer = new BinaryFormatter();
   7:         using (var ms = new MemoryStream())
   8:         {
   9:             serializer.Serialize(ms, obj);
  10:             ms.Position = 0;
  11:             cloned = (T)serializer.Deserialize(ms);
  12:         }
  13:         return cloned;
  14:     }
  15: }

 

In Silverlight you can write something similar and use the DataContractSerializer and the xml serialization:

 1: public static partial class Helpers
   2: {
   3:     public static T DeepClone<T>(this T obj)
   4:     {
   5:         T cloned = default(T);
   6:         var serializer = new DataContractSerializer(typeof(T));
   7:         using (var ms = new MemoryStream())
   8:         {
   9:             serializer.WriteObject(ms, obj);
  10:             ms.Position = 0;
  11:             cloned = (T)serializer.ReadObject(ms);
  12:         }
  13:         return cloned;        
  14:     }
  15: }

 

Due to the restriction of Silverlight the DataContractSerializer has some limitations and they depends if you are using or not the DataContract + DataMember attributes:
With no attributes you can only serialize types that have a default public constructor with no arguments and all public fields/properties, for example something like:

1: public class TestEntityNoAttributes
   2: {
   3:     public string PublicField;
   4:  
   5:     public TestEntityNoAttributes()
   6:     {
   7:     }
   8:  
   9:     public TestEntityNoAttributes(string pub)
  10:     {
  11:         PublicField = pub;
  12:     }
  13: }

 

Using DataContract + DataMember attribute you can choose what to serialize, you do not need the default constructor anymore, but still you are limited to public fields only...unless you use the InternalsVisibleTo attribute and convert the protected and private fields you want to serialize to internal, the class will look like:

1: [DataContract]
   2: public class TestEntityAttributes
   3: {
   4:     [DataMember]
   5:     public string PublicField;
   6:  
   7:     /// <summary>
   8:     /// changed from protected to internal
   9:     /// it has to be internal to allow serialization and be used with InternalsVisibleTo assembly attribute
  10:     /// </summary>
  11:     [DataMember]
  12:     internal string _ProtectedField;
  13:     public string ProtectedField
  14:     {
  15:         get { return _ProtectedField; }
  16:     }
  17:  
  18:     /// <summary>
  19:     /// changed from private to internal
  20:     /// it has to be internal to allow serialization and be used with InternalsVisibleTo assembly attribute
  21:     /// </summary>
  22:     [DataMember]
  23:     internal string _PrivateField;
  24:     public string PrivateField
  25:     {
  26:         get { return _PrivateField; }
  27:     }
  28:  
  29:     public TestEntityAttributes(string pub, string pro, string pri)
  30:     {
  31:         PublicField = pub;
  32:         _ProtectedField = pro;
  33:         _PrivateField = pri;
  34:     }
  35: }

 

plus you have to tag the assembly that holds the entities and make its internal members visibile to the serializer, since DataContractSerializer resides in the System.Runtime.Serialization you have to mark the assembly with:

 1: //needed to allow the serializer to access internal members
   2: [assembly: InternalsVisibleTo("System.Runtime.Serialization")]

 

Then using the Silverlight Unit Testing Framework from Jeff Wilcox, you can write a couple of tests like the following ones to verify that it works.

1: [TestMethod]
   2: public void TestEntityNoAttributeDeepClone()
   3: {
   4:     TestEntityNoAttributes e = new TestEntityNoAttributes("1");
   5:     TestEntityNoAttributes eCloned = e.DeepClone();
   6:     Assert.AreEqual(e.PublicField, eCloned.PublicField);
   7: }
   8:  
   9: [TestMethod]
  10: public void TestEntityAttributeDeepClone()
  11: {
  12:     TestEntityAttributes e = new TestEntityAttributes("1", "2", "3");
  13:     TestEntityAttributes eCloned = e.DeepClone();
  14:     Assert.AreEqual(e.PublicField, eCloned.PublicField);
  15:     Assert.AreEqual(e.ProtectedField, eCloned.ProtectedField);
  16:     Assert.AreEqual(e.PrivateField, eCloned.PrivateField);
  17: }

 

Naturally using both those methods you incur in performance penalty...but hey...this is the quick and dirty way after all.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值