C#系列——记一次业务需求:对象的深拷贝

简介:

 这篇随笔着实在意料之外,主要是源于上周开发BS的一个业务,需要用到对象的深拷贝。说的直白一点,就是将对象内存分配区和引用完全拷贝一份新的。这种需求以前就遇到过,怎么解决的已经记不清了。这次趁着这个机会将对象的深拷贝这个知识点记录下。

  先来说说业务场景,直接上代码:

复制代码
       //0.反射得到工厂属性
            var lstRes = new List<List<DragElementProp>>();
            var oType = typeof(Ewin.CommonLib.DtoModel.DTO_TM_PLANT);
            var lstAttr = ReflectorAttribute(oType);

            //1.给每个工厂对象的属性赋值,构造前台需要的数据结构
            var lstPropModel = oType.GetProperties();
            foreach (var oModel in lstModel)
            {
                var lstResTmp = new List<DragElementProp>();
                foreach (var oAttr in lstAttr)
                {
                    var oPropModel = lstPropModel.FirstOrDefault(x => x.Name == oAttr.Name);
                    if (oPropModel == null)
                    {
                        continue;
                    }
                    oAttr.Value = oPropModel.GetValue(oModel);
lstResTmp.Add(oAttr); }
lstRes.Add(lstResTmp); }
复制代码

需求就是lstAttr变量保存的是一个List<DragElementProp>类型的集合,需要遍历lstModel,需要将每一个的oModel的Name属性的值赋给lstAttr实例的Value属性。然后保存多个lstAttr的集合,形如List<List<DragElementProp>>。通过上面的代码在foreach (var oModel in lstModel)里面每次new一个新的var lstResTmp = new List<DragElementProp>();来保存赋值后lstAttr,明眼人一看就知道这种方式肯定不行,因为C#里面class是引用类型,每次改变的都是唯一的一个lstAttr实例,通过上面代码的方式得到的lstRes肯定会是几个相同的lstAttr,即最后一次赋值的lstAttr。

  怎么办?各种百度、各种博客园。查了多篇博文,发现答案千篇一律,深拷贝对象的三种解决方案:

  • 实现ICloneable接口,自定义拷贝功能
  • 序列化/反序列化类实现
  • 通过反射实现

 

我们逐一看看这几种方式

(1)实现ICloneable接口的方式,贴上园子里面的代码

复制代码
public class Person:ICloneable 
{ 
    public int Age { get; set; } 
    public string Address { get; set; } 
    public Name Name { get; set; } 
    public object Clone() 
    { 
      Person tem = new Person(); 
      tem.Address = this.Address; 
      tem.Age = this.Age; 
      tem.Name = new Name(this.Name.FristName, this.Name.LastName); 
      return tem; 
    } 
} 
复制代码

很显然,这种方式不可取。如果一个类里面有多个其他类成员,那不是每个都要去定义这样一个clone方法。太low。

 

(2)序列化反序列化方式。贴上园子里面的代码

复制代码
[Serializable] 
public class Person : ICloneable 

public
object Clone()   {     using (MemoryStream ms = new MemoryStream(1000))     {       object CloneObject;       BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));       bf.Serialize(ms, this);       ms.Seek(0, SeekOrigin.Begin);       // 反序列化至另一个对象(即创建了一个原对象的深表副本)       CloneObject = bf.Deserialize(ms);       // 关闭流       ms.Close();       return CloneObject;     }   }
}
复制代码

这种方式比上面方式好一点,但是需要对象是可序列化的,即要加上[Serializable]特性标签,博主试过如果一个普通的类调用这个方法会报异常。

博主用Newtonsoft.Json重新写了个:

复制代码
       foreach (var oModel in lstModel)
            {
                var lstResTmp = new List<DragElementProp>();
                foreach (var oAttr in lstAttr)
                {
                    var oPropModel = lstPropModel.FirstOrDefault(x => x.Name == oAttr.Name);
                    if (oPropModel == null)
                    {
                        continue;
                    }
                    oAttr.Value = oPropModel.GetValue(oModel);
                }
                //深拷贝一个集合到另一个集合
                var oJsonValue = Newtonsoft.Json.JsonConvert.SerializeObject(lstAttr);
                lstResTmp.AddRange(Newtonsoft.Json.JsonConvert.DeserializeObject<List<DragElementProp>>(oJsonValue));
                lstRes.Add(lstResTmp);
            }
复制代码

这种方式对对象没什么太特殊的要求。

 

(3)反射的方式,博主自己简单写了一个:

复制代码
     public static T CloneModel<T>(T oModel)
        {
            var oRes = default(T);
            var oType = typeof(T);

            //得到新的对象对象
            oRes = (T)Activator.CreateInstance(oType);

            //给新的对象复制
            var lstPro = oType.GetProperties();
            foreach (var oPro in lstPro)
            {
                //从旧对象里面取值
                var oValue = oPro.GetValue(oModel);

                //复制给新的对象
                oPro.SetValue(oRes, oValue);
            }

            return oRes;
        }
复制代码

这种方式也比较简单,但考虑到反射得性能问题,而且如果是clone集合,需要遍历去反射这样效率就更低。

 

  综上所述:要深拷贝一个对象,其实上述无论哪种方式都是新产生一个对象,然后给新的对象依次赋值来实现。方案一一般不可取,方案二在集合的序列化方便可能效率稍微高点,方案三如果只是简单的拷贝一个对象我觉得也是不错的选择。反正博主更加偏好方案二,用起来简单。

  

  反正找了好久说的都这三种方式,这次先记录下,如果没有更好的方式就用这些方案先解决吧,当然,如果以后知道了更好的方式也可以拿出来和大家分享。也不知道.Net是否预留了某些特殊的通道来处理这种深拷贝。希望知道的大侠多多指教~~






本文转自懒得安分博客园博客,原文链接:http://www.cnblogs.com/landeanfen/p/4678534.html,如需转载请自行联系原作者

目录
相关文章
|
XML C# 数据格式
C#中实现对象的深拷贝
原文:C#中实现对象的深拷贝 深度拷贝指的是将一个引用类型(包含该类型里的引用类型)拷贝一份(在内存中完完全全是两个对象,没有任何引用关系)..........  直接上代码: 1 /// 2 /// 对象的深度拷贝(序列化的方式) 3 /// 4 ...
1622 0
|
C# 数据格式 XML
.NET[C#]中实现实体对象深拷贝(克隆/复制)的几种方法
.NET[C#]中实现实体对象深拷贝(克隆/复制)的几种方法,总有一种适合你。 方式一 使用二进制流 using System; using System.IO; using System.Runtime.
4036 0
|
存储 C#
C#序列化实现深拷贝及原理
一、基本概念 1、深拷贝 深拷贝要求将要clone的对象的引用类型成员对象也拷贝到新对象中,而不是拷贝这些对象的引用(浅拷贝)。 2、序列化 将对象或对象图形转换成线性字节序列,以存储或传输到另一个位置的过程。
1249 0
|
C#
编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]
前言   本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:   建议13、为类型输出格式化字符串   建议14、正确实现浅拷贝和深拷贝   建议15、使用dynamic来简化反射实现 建议13、为类型输出格式化字符串    有两种方法可以为类型提供格式化的字符串输出。
791 0
|
C# C++ 编译器
浅析C#深拷贝与浅拷贝(转)
1.深拷贝与浅拷贝   拷贝即是通常所说的复制(Copy)或克隆(Clone),对象的拷贝也就是从现有对象复制一个“一模一样”的新对象出来。虽然都是复制对象,但是不同的 复制方法,复制出来的新对象却并非完全一模一样,对象内部存在着一些差异。
1249 0
|
C#
编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]
原文:编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射] 前言   本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。
834 0