C#深浅拷贝的深入解析

前言

前面我们学习完了设计模式,在其中我们有了解到原型模式。这里涉及到了克隆自身对象。那么也就是对对象进行拷贝。这里就涉及到了这么一个概念。深浅拷贝、何为深拷贝何为浅拷贝呢?我们一起来看看吧。

浅拷贝

首先我们看看浅拷贝。浅拷贝就是将对象中的所有字段复制到新对象中去,浅拷贝对于值类型和引用类型有不同的影响。值类型的值被复制到副本中后,修改副本中的值不会影响原来对象的值。然而引用类型被复制到副本中的是引用类型的引用。不是引用的对象。这样再修改副本中的值是会导致原来对象的值也被修改了。但是这里引用类型情况我们需要排除字符串String类型。

那么为何引用类型修改副本的值会造成原来对象的值的变化,而string字符串类型却排除在外呢?首先我们需要知道这么一个概念,string类型是一个不可变的数据类型,也就是意味着对字符串对象进行了初始化,该字符串对象就不能改变了。表面上我们修改字符串的内容的方法和运算实际上是创建了一个新字符串,然后根据需要可以把旧字符串的内容复制到新字符串中。怎么理解你?我们看下面这个案例:

  #region 字符串比较
  /// <summary>
  /// 获取引用类型的内存地址方法
  /// </summary>
  /// <param name="o"></param>
  /// <returns></returns>
  public static string getMemory(object o)
  {
   GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned);
   IntPtr addr = h.AddrOfPinnedObject();
   return "0x" + addr.ToString("X");
  }
  /// <summary>
  /// 字符串比较
  /// </summary>
  public static void Compares()
  {
   string a = "123";
   Console.WriteLine("a的引用地址:\t\t" + getMemory(a));
   string b = "123";
   Console.WriteLine("b的引用地址:\t\t" + getMemory(b));
   Console.WriteLine("a与b的比较:\t\t" + Object.ReferenceEquals(a, b));
   b = "456";
   Console.WriteLine("b的引用地址:\t\t" + getMemory(b));

  }

  #endregion

这里我们看a=”123”,b=”123”。我们看他们的引用地址是一样的。也就是说我们先创建a的时候创建了字符串a,有了一个引用地址。然后我们创建b的时候首先会寻找是否存在相同的值。如果存在相同的值就获取其引用地址。这也就是为什么a与b的引用地址是一样的。这里涉及到一个叫做字符驻留池的东西。会对字符串进行保存。那么后面我们修改b的值然后输出其引用地址,发现和之前的引用地址不一样。说明并不是修改原来的值,而是重新创建了一个字符串,重新获取了它的引用地址。

我们接下来看一个浅拷贝的案例吧,首先我们准备的是以下的数据类型的值:int,string,enum,struct,class,int[],string[]。

 /// <summary>
 /// 枚举
 /// </summary>
 public enum EnumTest
 {
  TestOne = 1,
  TestTwo = 2
 }

 /// <summary>
 /// 结构体
 /// </summary>
 public struct StructTest
 {
  public int Test;
  public StructTest(int i)
  {
   Test = i;
  }
 }

 /// <summary>
 /// 类
 /// </summary>
 public class ClassTest
 {
  public string TestString;
  public ClassTest(string _string)
  {
   TestString = _string;
  }
 }
 /// <summary>
 /// 深拷贝
 /// </summary>
 public class DeepClone : ICloneable
 {
  public int _int = 1;
  public string _string = "1";
  public EnumTest _enum = EnumTest.TestOne;
  public StructTest _struct = new StructTest(1);
  public ClassTest _class = new ClassTest("1");
  public int[] arrInt = new int[] { 1 };
  public string[] arrString = new string[] { "1" };
  public object Clone()
  {
   var NewOne = JsonConvert.SerializeObject(this);
   return JsonConvert.DeserializeObject<DeepClone>(NewOne);
  }
 }
 class Program
 {
  static void Main(string[] args)
  {
   DeepClone simple = new DeepClone();
   var simpleTwo = (DeepClone)simple.Clone();
   simpleTwo._int = 2;
   simpleTwo._string = "2";
   simpleTwo._enum = EnumTest.TestTwo;
   simpleTwo._struct.Test = 2;
   simpleTwo._class.TestString = "2";
   simpleTwo.arrInt[0] = 2;
   simpleTwo.arrString[0] = "2";

   Console.WriteLine($"int 类型变化  原对象:{simple._int}\t\t    备份对象:{simpleTwo._int}");
   Console.WriteLine($"string 类型变化 原对象:{simple._string}\t\t   备份对象:{simpleTwo._string}");
   Console.WriteLine($"enum 类型变化 原对象:{(int)simple._enum}\t\t   备份对象:{(int)simpleTwo._enum}");
   Console.WriteLine($"struct 类型变化 原对象:{simple._struct.Test}\t\t  备份对象:{simpleTwo._struct.Test}");
   Console.WriteLine($"class 类型变化 原对象:{simple._class.TestString}\t\t 备份对象:{simpleTwo._class.TestString}");
   Console.WriteLine($"int数组 类型变化 原对象:{simple.arrInt[0]}\t\t   备份对象:{simpleTwo.arrInt[0]}");
   Console.WriteLine($"string数组 类型变化 原对象:{simple.arrString[0]}\t\t 备份对象:{simpleTwo.arrString[0]}");
  }
 }

我们通过继承ICloneable接口对这些类型都进行了浅拷贝然后修改副本对象。输出原对象和副本对象进行比较。我们发现int,enum,struct、值类型以及string这个特殊的引用类型的原对象值没有被影响改变。但是class,int[],string[]这些引用类型对象原对象被影响改变了值。也就再次验证了我们前面说的。浅拷贝是将对象进行赋值到一个副本对象中去,值类型复制值,引用类型复制其引用对象。修改副本对象值,值类型和string原对象不会被影响改变,引用类型除string其原对象都会被影响改变。

深拷贝

我们上面看了浅拷贝,浅拷贝还是有一定的影响的,处理不好可能就成bug。那么我们看看对应的深拷贝又是什么样的呢?这里可以先声明,深拷贝对值类型和引用类型都没有区别对待。深拷贝也是将对象中的所有字段复制到新对象中去,但是对象无论是值类型还是引用类型都将被重新创建然后复制到副本对象去。对于副本对象的修改将不会影响到原对象,无论任何类型。

我们继续将上面的例子进行深拷贝看看:

 /// <summary>
 /// 深拷贝
 /// </summary>
 public class DeepClone : ICloneable
 {
  public int _int = 1;
  public string _string = "1";
  public EnumTest _enum = EnumTest.TestOne;
  public StructTest _struct = new StructTest(1);
  public ClassTest _class = new ClassTest("1");
  public int[] arrInt = new int[] { 1 };
  public string[] arrString = new string[] { "1" };
  public object Clone()
  {
   var NewOne = JsonConvert.SerializeObject(this);
   return JsonConvert.DeserializeObject<DeepClone>(NewOne);
  }
 }

 class Program
 {
  static void Main(string[] args)
  {
   DeepClone simple = new DeepClone();
   var simpleTwo = (DeepClone)simple.Clone();
   simpleTwo._int = 2;
   simpleTwo._string = "2";
   simpleTwo._enum = EnumTest.TestTwo;
   simpleTwo._struct.Test = 2;
   simpleTwo._class.TestString = "2";
   simpleTwo.arrInt[0] = 2;
   simpleTwo.arrString[0] = "2";

   Console.WriteLine($"int 类型变化  原对象:{simple._int}\t\t    备份对象:{simpleTwo._int}");
   Console.WriteLine($"string 类型变化 原对象:{simple._string}\t\t   备份对象:{simpleTwo._string}");
   Console.WriteLine($"enum 类型变化 原对象:{(int)simple._enum}\t\t   备份对象:{(int)simpleTwo._enum}");
   Console.WriteLine($"struct 类型变化 原对象:{simple._struct.Test}\t\t  备份对象:{simpleTwo._struct.Test}");
   Console.WriteLine($"class 类型变化 原对象:{simple._class.TestString}\t\t 备份对象:{simpleTwo._class.TestString}");
   Console.WriteLine($"int数组 类型变化 原对象:{simple.arrInt[0]}\t\t   备份对象:{simpleTwo.arrInt[0]}");
   Console.WriteLine($"string数组 类型变化 原对象:{simple.arrString[0]}\t\t 备份对象:{simpleTwo.arrString[0]}");
  }
 }

这里我们看这个运行结果,无论值类型还是引用类型修改副本对象之后都没有影响原对象的值。这也就是深拷贝的特点了。

总结

我们看完了浅拷贝与深拷贝,我们仔细回顾下。浅拷贝将对象的字段复制到新的对象中去,但是当修改新对象的时候,值类型和string类型的字段将不会影响原对象的字段,而引用类型除string类型外都将影响原对象的值。深拷贝也是将对象的字段复制到新的对象中去,但是无论是值类型还是引用类型的改变都不会影响原对象的值。因为深拷贝是将原对象重新创建然后复制到副本对象中去的。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • c#深拷贝文件夹示例

    复制代码 代码如下: using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;using System.Text.RegularExpressions;using System.Threading.Tasks; namespace FileUtility{    public class Program    {        public static v

  • C#中序列化实现深拷贝,实现DataGridView初始化刷新的方法

    winfrom中DataGridView在的单元格在编辑时候会修改它的数据源的,如果我们遇到这样一种情景,刷新数据源到原始状态,这个时候要么数据源的重新获取绑定,要么通过拷贝一份原始档的数据再绑定处理,这里介绍拷贝方式处理. 大致代码如下: 1.目标对需要序列化,并实现ICloneable 接口: [Serializable] public class DtoColumn : ICloneable2.实现接口方法Clone: public object Clone() { using (Memo

  • 简单谈谈C#中深拷贝、浅拷贝

    Object.MemberwiseClone 方法 创建当前 Object 的浅表副本. protected Object MemberwiseClone() MemberwiseClone 方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象. 如果字段是值类型的,则对该字段执行逐位复制. 如果字段是引用类型,则复制引用但不复制引用的对象:因此,原始对象及其复本引用同一对象. 例如,考虑对象X引用对象 A 和 B , 对象 B 依次引用对象 C. X 的浅表副本

  • C#浅拷贝和深拷贝实例解析

    在有些时候,我们需要从数据库读取数据填充对象或从硬盘读取文件填充对象,但是这样做相对耗时.这时候我们就想到了对象的拷贝.本文即以实例形式解析了C#浅拷贝和深拷贝的用法.具体如下: 一.浅拷贝 1.什么是"浅拷贝": 当针对一个对象前拷贝的时候,对于对象的值类型成员,会复制其本身,对于对象的引用类型成员,仅仅复制对象引用,这个引用指向托管堆上的对象实例. 2.有一个对象,包含引用类型的类成员和值类型的struct成员 Cinema包含引用类型成员Room和值类型成员Film. publi

  • 一种c#深拷贝方式完胜java深拷贝(实现上的对比分析)

    楼主是一名asp.net攻城狮,最近经常跑java组客串帮忙开发,所以最近对java的一些基础知识特别上心.却遇到需要将一个对象深拷贝出来做其他事情,而原对象保持原有状态的情况.(实在是不想自己new一个出来,然后对着一堆字段赋值......好吧,再此之前我没有关心是否项目框架有深拷贝的方法),然后就想着用反射实现吧....接下来 是我自己的原因,还是真的不存在这样的纯用反射实现的深拷贝方式....(c#是有纯反射实现的) 但也不能算自己白忙活吧,也找到了其他实现深拷贝的方式(但是每种方式我都觉

  • C#中4种深拷贝方法介绍

    1:利用反射实现 public static T DeepCopy<T>(T obj) { //如果是字符串或值类型则直接返回 if (obj is string || obj.GetType().IsValueType) return obj; object retval = Activator.CreateInstance(obj.GetType()); FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | B

  • c# 深拷贝与浅拷贝的区别分析及实例

    深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响.举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人.比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等. 浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同).对其中任何一个对象的改动都会影响另外一个对象.举个例子,一个人一开始叫张三,后来

  • C#中使用DataContractSerializer类实现深拷贝操作示例

    一.实现深拷贝方法 using System.IO; using System.Runtime.Serialization; namespace DeepCopyExp { class DeepCopy { public static T DeepCopyByDCS<T>(T obj) { T newObject; using (MemoryStream memoryStream = new MemoryStream()) { DataContractSerializer dcs = new

  • C#深浅拷贝的深入解析

    前言 前面我们学习完了设计模式,在其中我们有了解到原型模式.这里涉及到了克隆自身对象.那么也就是对对象进行拷贝.这里就涉及到了这么一个概念.深浅拷贝.何为深拷贝何为浅拷贝呢?我们一起来看看吧. 浅拷贝 首先我们看看浅拷贝.浅拷贝就是将对象中的所有字段复制到新对象中去,浅拷贝对于值类型和引用类型有不同的影响.值类型的值被复制到副本中后,修改副本中的值不会影响原来对象的值.然而引用类型被复制到副本中的是引用类型的引用.不是引用的对象.这样再修改副本中的值是会导致原来对象的值也被修改了.但是这里引用类

  • 浅谈Python对内存的使用(深浅拷贝)

    本文主要研究的是Python对内存的使用(深浅拷贝)的相关问题,具体介绍如下. 浅拷贝就是对引用的拷贝(只拷贝父对象) 深拷贝就是对对象的资源的拷贝 >>> a=[1,2,3,'a','b'] >>> b=a >>> b [1, 2, 3, 'a', 'b'] >>> a [1, 2, 3, 'a', 'b'] >>> id(a) 3021737547592 >>> id(b) 3021737547

  • JS赋值、浅拷贝和深拷贝(数组和对象的深浅拷贝)实例详解

    本文实例讲述了JS赋值.浅拷贝和深拷贝(数组和对象的深浅拷贝).分享给大家供大家参考,具体如下: 深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的.  浅拷贝 只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做浅拷贝(浅复制) 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存.但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象. 赋值和浅拷贝的区别 当我们把一个对象赋值给一个新的变

  • Python基础学习之深浅拷贝问题及递归函数练习

    目录 一.深浅拷贝问题 二.递归函数练习 1. 求阶乘 2. 猴子吃桃问题 3. 打印斐波那契数列 一.深浅拷贝问题 在实际工作中,经常涉及到数据的传递,在数据传递使用过程中,可能会发生数据被修改的问题.为了防止数据被修改,就需要在传递一个副本,即使副本被修改,也不会影响原数据的使用.为了生成这个副本,就产生了拷贝.下面先了解一下几个概念:对象.可变类型.引用 Python对象:在 Python 中,对象有一种很通俗的说法是,万物皆对象.说的就是构造的任何数据类型都是一个对象,无论是数字,字符串

  • 浅谈js基础数据类型和引用类型,深浅拷贝问题,以及内存分配问题

    js 深浅拷贝问题 浅拷贝一般指的是基本类型的复制 深拷贝一般指引用类型的拷贝,把引用类型的值也拷贝出来 举例 h5的sessionStorage只能存放字符串,所以要存储json时就要把json使用JSON.stringify()把json转换成string,然后再用JSON.parse()转换成json数据 缺点:JSON.parse和JSON.stringify只支持IE9+以上 解决这个问题可以使用深度比那里拷贝方法 js 中内存分配问题(堆和栈) js中基本类型类型一般是存储在栈中的.

  • 详解iOS的深浅拷贝

    前言 OC对象的三种拷贝方式 OC的对象拷贝有如下三种方式,很多时候我们把深复制和完全复制混为一谈,其他他们是有区别的,具体如下 浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制. 深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制. 完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制. 两图以避之 理解深复制(mutableCopy) 浅复制很简单,就不演示了,看

  • 浅析Python中的赋值和深浅拷贝

    python中,A object  = B object  是一种赋值操作,赋的值不是一个对象在内存中的空间,而只是这个对象在内存中的位置 . 此时当B对象里面的内容发生更改的时候,A对象也自然而然的会跟着更改. name = ["root","admin"] cp_name = name # 对cp_name进行赋值操作 # 对name列表进行插入 name.append('root_temp') print(name,cp_name) # ['root', 'a

  • c++中深浅拷贝以及写时拷贝的实现示例代码

    本文主要给大家介绍了关于c++中深浅拷贝及写时拷贝实现的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 一:浅拷贝&深拷贝 浅拷贝:在拷贝构造的时候,直接将原内容的地址交给要拷贝的类,两个类共同指向一片空间.但是存在很大的缺陷:①一旦对s2进行操作,s1的内容也会改变:②析构时先析构s2,再析构s1,但是由于s1,s2指向同一片空间,会导致一片空间的二次析构导致出错. 深拷贝:通过开辟和源空间大小相同的空间并将内容拷贝下来再进行操作.不论是否对s2进行操作,都会拷贝一片相

  • JavaScript基础心法 深浅拷贝(浅拷贝和深拷贝)

    前言 说到深浅拷贝,必须先提到的是JavaScript的数据类型,之前的一篇文章JavaScript基础心法--数据类型说的很清楚了,这里就不多说了. 需要知道的就是一点:JavaScript的数据类型分为基本数据类型和引用数据类型. 对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言的. 浅拷贝 浅拷贝的意思就是只复制引用,而未复制真正的值. const originArray = [1,2,3,4,5]; const originObj = {a:'a'

  • js中的深浅拷贝问题简析

    前言 在开发过程中,偶尔会遇到这种场景,拿到一个数据后,你打算对它进行处理,但是你又希望拷贝一份副本出来,方便数据对比和以后恢复数据. 那么这就涉及到了 JS 中对数据的深浅拷贝问题,所谓深浅拷贝,浅拷贝的意思就是,你只是复制了对象数据的引用,并没有把内存里的值另外复制一份,那么深拷贝就是把值完整地复制一份新的值. 下面这篇文章就对js中的深浅拷贝进行了深入的讲解,下面话不多说了,来一起看看详细的介绍吧 问题描述: 因为在JavaScript中对象在赋值中存储的是对象的地址(指针),所以会造成对

随机推荐