这篇随笔着实在意料之外,主要是源于上周开发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())
    {
      object CloneObject;
      BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
      bf.Serialize(ms, this);
      ms.Seek(, 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是否预留了某些特殊的通道来处理这种深拷贝。希望知道的大侠多多指教~~

C#系列——记一次业务需求:对象的深拷贝的更多相关文章

  1. 分布式ID系列之为什么需要分布式ID以及生成分布式ID的业务需求

    为什么需要分布式id生成系统 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在美团点评的金融.支付.餐饮.酒店.猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID ...

  2. 大数据技术之_25_手机APP信息统计系统项目_01_APP 数据生成模块 + 数据收集模块 + 数据处理模块框架搭建 + 业务需求处理 + 数据展示模块 +项目总结 + 问题总结

    一 项目概述1.1 角色1.2 业务术语1.3 项目效果展示二 项目需求三 项目概要3.1 项目技术架构3.2 项目目录结构3.3 项目技术选型3.4 项目整体集群规划3.5 创建项目工程四 APP ...

  3. DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对?

    写在前面 阅读目录: 具体业务场景 业务需求变化 "愚蠢"的应对 消息列表实现 消息详情页实现 消息发送.回复.销毁等实现 回到原点的一些思考 业务需求变化,领域模型变化了吗? 对 ...

  4. DDD 领域驱动设计-看我如何应对业务需求变化,领域模型调整?

    写在前面 上一篇:DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对? "愚蠢的应对",这个标题是我后来补充上的,博文中除了描述需求变化.愚蠢应对和一些思考,确实没有实质性 ...

  5. Spring 定时操作业务需求

    1.定时分析 在业务需求中有的需要检测用户的状态,通过对用户状态的检测做出对此状态相应的操作,如果这种检测由运营人工检测,不仅工作量大,而且准确性不高,人工无法很好的完成工作: 问题根源:在检测用户状 ...

  6. java编程排序之自定义类型的集合,按业务需求排序

    自定义引用类型放入集合中,按实际业务需求进行排序的两种思路 第一种思路: (1)自定义实体类实现java.lang.Comparable接口,重写public int compareTo(Object ...

  7. EasySharding.EFCore 如何设计使用一套代码完成的EFCore Migration 构建Saas系统多租户不同业务需求且满足租户自定义分库分表、数据迁移能力?

    下面用一篇文章来完成这些事情 多租户系统的设计单纯的来说业务,一套Saas多租户的系统,面临很多业务复杂性,不同的租户存在不同的业务需求,大部分相同的表结构,那么如何使用EFCore来完成这样的设计呢 ...

  8. 我眼中BA(业务需求分析师)的技能广度和深度

    BA,或者称业务分析师,是企业数字能力和业务能力之间的沟通桥梁.随着企业数字转型的进一步深化,相信对BA这样的技能需求会越来越多,只是未必都用“BA/业务分析师”这样的Title. ThoughtWo ...

  9. 【Xamarin挖墙脚系列:典型的业务程序的结构搭建】

    原文:[Xamarin挖墙脚系列:典型的业务程序的结构搭建] 其实app就是客户端.在现代的程序中,都是典型的C/S结构.当然,一些离线的小游戏,功能性应用除外,如:电话本,通信录,短信查看等等 这个 ...

随机推荐

  1. HTTP 协议整理(转)

    HTTP 协议 作为web开发人员,了解一些http协议的知识很有必要.本文简单介绍了HTTP协议的知识,若有错误的地方,望大家斧正. 1.HTTP协议是什么? http协议是一个应用层的协议.规定了 ...

  2. php开发公众号 token验证失败 其中一个原因

    断断续续,弄了好几天,索性一狠心花了三个小时,总算找出问题了. "token验证失败" 可能原因有很多种,其他网友已经几乎穷尽了,但是我所遇到的在网络上没有看到,所以这里记录下. ...

  3. window对象的属性及事件。

    不同的运行环境有不同的“顶层对象”,而在浏览器的环境中,顶层对象就是window对象.window就是指当前的浏览器窗口. 例:var a = 1: window.a; //1 1.window对象的 ...

  4. jeecg环境搭建20160707

    1.首页修改位置:src/main/webapp/webpage/main 2.tomcat45秒超时启动修改,open打开servers项目,在右上角处的timeouts参数修改: 3.eclips ...

  5. One-Time Project Recognition

    Please indicate the source if you need to repost. After implementing NetSutie for serveral companies ...

  6. React Native知识6-NavigatorIOS组件

    NavigatorIOS包装了UIKit的导航功能,可以使用左划功能来返回到上一界面.本组件并非由Facebook官方开发组维护.这一组件的开发完全由社区主导.如果纯js的方案能够满足你的需求的话,那 ...

  7. Orchard中如何配置远端发布

    Orchard中默认安装是有Blog功能的.下面介绍如何配置Remote Blog Publishing功能,使用Windows Live Writer客户端发布博客. 一,开启Remote Blog ...

  8. Windows 10 IoT Serials 3 - Windows 10 IoT Core Ardunio Wiring Mode

    Maker社区和智能硬件的朋友一定知道Arduino,很多3D打印机都是用它做的.为了迎合这一大块市场,微软在基于Intel Galileo的Windows 8.1 IoT中就是使用这种基于Ardui ...

  9. ORACLE的SPFILE与PFILE

    ORACLE中的参数文件是一个包含一系列参数以及参数对应值的操作系统文件,可以分为两种类型.它们是在数据库实例启动时候加载的,决定了数据库的物理结构.内存.数据库的限制及系统大量的默认值.数据库的各种 ...

  10. Linux命令学习总结:cp命令

    命令简介: cp命令用来复制文件或目录.指令英文原义:copy 指令所在路径:/bin/cp 命令语法: Usage: cp [OPTION]... [-T] SOURCE DEST or: cp [ ...