服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#中深拷贝和浅拷贝的介绍与用法

C#中深拷贝和浅拷贝的介绍与用法

2023-02-22 16:53.NET开发菜鸟 C#

本文详细讲解了C#中深拷贝和浅拷贝的介绍与用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

一、什么是深拷贝和浅拷贝

对于所有面向对象的语言,复制永远是一个容易引发讨论的题目,C#中也不例外。此类问题在面试中极其容易被问到,我们应该在了解浅拷贝和深拷贝基本概念的基础上,从设计的角度进一步考虑如何支持对象的拷贝。

在System.Object类中,有一个受保护的方法object.MemberwiseClone(),这个方法实现了对象的复制。事实上,它所实现的就是我们所称的浅拷贝。

深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。比如一个黄狗叫大黄,使用克隆术克隆另外一个黄狗叫小黄,这样大黄和小黄就相对独立了,他们不互相影响。在.NET中int,double以及结构体和枚举等。

int a=12;
int c=a;//进行了深拷贝
c=232 //不影响

浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。就像一个人改名了一样,他还是这个人,只不过名字变了而已。

public class YDog
  {
      public string Name { get; set; }
  }
  class Program
  {
      static void Main(string[] args)
      {
          YDog sourceP = new YDog() { Name = "大黄" };
          YDog copyP = sourceP; // 浅拷贝
          copyP.Name = "小黄"; // 拷贝对象改变Name值
          // 结果都是"小黄",因为实现的是浅拷贝,一个对象的改变都会影响到另一个对象
          Console.WriteLine("YDog.Name: [SourceP: {0}] [CopyP:{1}]", sourceP.Name, copyP.Name);
          Console.Read();
      }
  }

所谓的浅拷贝,是指拷贝一个对象的时候,拷贝原始对象中所有的非静态值类型成员和所有的引用类型成员的引用。换言之,新的对象和原始对象将共享所有引用类型成员的实际对象。而相对的,深拷贝是指不仅复制所有的非静态值类型成员,而且也复制所有引用类型成员的实际对象。深拷贝和浅拷贝的概念是递归的,也就是说当引用类型成员中包含另外一个引用类型成员时,拷贝的时候将对其内部成员实行同样的复制策略。

浅拷贝示意图如下所示:

C#中深拷贝和浅拷贝的介绍与用法

深拷贝示意图如下图所示:

C#中深拷贝和浅拷贝的介绍与用法

类型基类System.Object已经为所有类型都实现了浅拷贝,类型所要做的就是公开一个复制的接口,而通常的,这个接口会借由实现ICloneable接口来实现。ICLoneable只包含一个Clone方法。该方法既可以被实现为浅拷贝也可以被实现为深拷贝,具体如何取舍需要根据具体类型的需求来决定。下面的代码提供了一个深拷贝的简单示例:

using System;

namespace DeepCopy
{
  class Program
  {
      static void Main(string[] args)
      {
          // 定义原始对象
          DpCopy dc = new DpCopy();
          dc._i = 10;
          dc._a = new A();

          // 定义深拷贝对象
          DpCopy deepClone = (DpCopy)dc.Clone();
          // 定义浅拷贝对象
          DpCopy shadowclone = (DpCopy)dc.MemberwiseClone();
          // 深拷贝的复制对象将拥有自己的引用类型成员对象
          // 所以这里的赋值不会影响原始对象
          deepClone._a._s = "我是深拷贝的A";
          Console.WriteLine(dc);
          Console.WriteLine(deepClone);
          Console.WriteLine("\r\n");

          // 浅拷贝的复制对象共享原始对象的引用类型成员对象
          // 所以这里的赋值将影响原始对象
          shadowclone._a._s = "我是浅拷贝的A";
          Console.WriteLine(dc);
          Console.WriteLine(shadowclone);

          Console.ReadKey();
      }
  }

  public class DpCopy : ICloneable
  {
      public int _i = 0;
      public A _a = new A();
      public object Clone()
      {
          // 实现深拷贝
          DpCopy newDc = new DpCopy();
          // 重新实例化一个引用类型变量
          newDc._a = new A();
          // 给新引用类型变量的成员值
          newDc._a._s = _a._s;
          newDc._i = _i;
          return newDc;
      }

      // 实现浅拷贝
      public new object MemberwiseClone()
      {
          return base.MemberwiseClone();
      }

      /// <summary>
      /// 重写类的ToString()方法
      /// </summary>
      /// <returns></returns>
      public override string ToString()
      {
          return "I的值为:" + _i.ToString() + ",A为:" + _a._s;
      }
  }

  /// <summary>
  /// 包含一个引用成员的类型
  /// </summary>
  public class A
  {
      public string _s = "我是原始A";
  }
}

在上面的代码中,类型DpCopy通过ICLoneable接口的Clone方法提供了深拷贝,并且通过提供一个MemberwiseClone的公共方法提供了浅拷贝。DpCopy类型具有一个值类型成员和一个引用类型成员,引用类型成员在浅拷贝和深拷贝时将展现不同的特性,浅拷贝的原始对象和目标对象公用了一个引用类型成员对象,这在程序的执行结果中可以清楚地看到:

C#中深拷贝和浅拷贝的介绍与用法

有的参考资料上说C#中的深拷贝通过ICloneable接口来实现。这句话并不正确。事实上任何名字的方法都可以用来实现深拷贝,并且没有任何语法来规定深拷贝只能通过Clone方法来实现。Clone这个名字只是一种习惯的称呼,而实现ICloneable只能带来一般接口的通用便利性,而并没有任何关于拷贝的特殊性。

一般可被继承的类型应该避免实现ICloneable接口,因为这样做将强制所有的子类型都需要实现ICloneable接口,否则将使类型的深拷贝不能覆盖子类的新成员。

实现深拷贝

1、新建一个对象,一个一个的重新赋值,麻烦一点

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ServiceTest
{
  public class Program
  {
      static void Main(string[] args)
      {

          YDog Dog = new YDog() { Name = "大黄" };
          YDog NewDog = new YDog();
          NewDog.Name = Dog.Name;

          Console.WriteLine($"Dog.Name:{Dog.Name},NewDog.Name:{NewDog.Name}");
          Console.Read();
      }
  }
}

输出结果

C#中深拷贝和浅拷贝的介绍与用法

2、利用反射实现深拷贝

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ServiceTest
{
  public class Program
  {
      static void Main(string[] args)
      {

          YDog Dog = new YDog() { Name = "大黄" };
          YDog NewDog = (YDog)DeepCopy(Dog);
          NewDog.Name = Dog.Name;

          Console.WriteLine($"Dog.Name:{Dog.Name},NewDog.Name:{NewDog.Name}");
          Console.Read();
      }

      /* 利用反射实现深拷贝*/
      public static object DeepCopy(object _object)
      {
          Type T = _object.GetType();
          object o = Activator.CreateInstance(T);
          PropertyInfo[] PI = T.GetProperties();
          for (int i = 0; i < PI.Length; i++)
          {
              PropertyInfo P = PI[i];
              P.SetValue(o, P.GetValue(_object));
          }
          return o;
      }
  }
}

输出结果

C#中深拷贝和浅拷贝的介绍与用法

 

3、利用序列化和反序列化来实现,如下代码

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace ServiceTest
{
  public class Program
  {
      static void Main(string[] args)
      {

          YDog Dog = new YDog() { Name = "大黄" };
          //YDog NewDog = (YDog)DeepCopy(Dog);
          //NewDog.Name = Dog.Name;

          // 序列化实现
          YDog NewDog = (YDog)DeepCopy<YDog>(Dog);

          Console.WriteLine($"Dog.Name:{Dog.Name},NewDog.Name:{NewDog.Name}");
          Console.Read();
      }

      /* 利用反射实现深拷贝*/
      public static object DeepCopy(object _object)
      {
          Type T = _object.GetType();
          object o = Activator.CreateInstance(T);
          PropertyInfo[] PI = T.GetProperties();
          for (int i = 0; i < PI.Length; i++)
          {
              PropertyInfo P = PI[i];
              P.SetValue(o, P.GetValue(_object));
          }
          return o;
      }

      // 利用XML序列化和反序列化实现
      public static T DeepCopyWithXmlSerializer<T>(T obj)
      {
          object retval;
          using (MemoryStream ms = new MemoryStream())
          {
              XmlSerializer xml = new XmlSerializer(typeof(T));
              xml.Serialize(ms, obj);
              ms.Seek(0, SeekOrigin.Begin);
              retval = xml.Deserialize(ms);
              ms.Close();
          }


          return (T)retval;
      }


      // 利用二进制序列化和反序列实现
      public static T DeepCopyWithBinarySerialize<T>(T obj)
      {
          object retval;
          using (MemoryStream ms = new MemoryStream())
          {
              BinaryFormatter bf = new BinaryFormatter();
              // 序列化成流
              bf.Serialize(ms, obj);
              ms.Seek(0, SeekOrigin.Begin);
              // 反序列化成对象
              retval = bf.Deserialize(ms);
              ms.Close();
          }


          return (T)retval;
      }

      public static T DeepCopy<T>(T obj)
      {
          // 序列化
         string json= JsonConvert.SerializeObject(obj);
          // 反序列化
         return JsonConvert.DeserializeObject<T>(json);
      }
  }
}

 

二、总结

浅拷贝是指复制类型中的所有值类型成员,而只赋值引用类型成员的引用,并且使目标对象共享原对象的引用类型成员对象。深拷贝是指同时复制值类型成员和引用类型成员的对象。浅拷贝和深拷贝的概念都是递归的。System.Object中的MemberwiseClone已经实现了浅拷贝,但它是一个受保护的方法。无论深拷贝还是浅拷贝,都可以通过实现ICloneable接口的Clone方法来实现,可被继承的类型需要谨慎地实现ICloneable接口,因为这将导致所有的子类型都必须实现ICloneable接口。

到此这篇关于C#中深拷贝和浅拷贝的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/dotnet261010/p/12329220.html

延伸 · 阅读

精彩推荐
  • C#C#面向对象的23种设计模式介绍

    C#面向对象的23种设计模式介绍

    这篇文章介绍了C#面向对象的23种设计模式,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...

    Run294810182023-02-08
  • C#C# lambda表达式应用如何找出元素在list中的索引

    C# lambda表达式应用如何找出元素在list中的索引

    这篇文章主要介绍了C# lambda表达式应用如何找出元素在list中的索引的相关资料,需要的朋友可以参考下...

    埃罗芒老兄6222022-02-19
  • C#利用C#9.0新语法如何提升if语句美感

    利用C#9.0新语法如何提升if语句美感

    这篇文章主要给大家介绍了关于利用C# 9.0新语法如何提升if语句美感的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    精致码农 • 王亮4002022-10-14
  • C#C# 字符串、数组和List的截取和转换实例

    C# 字符串、数组和List的截取和转换实例

    下面小编就为大家分享一篇C# 字符串、数组和List的截取和转换实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    杜子烟9052022-02-10
  • C#C#开发WinForm根据条件改变DataGridView行颜色

    C#开发WinForm根据条件改变DataGridView行颜色

    这篇文章介绍了C#开发WinForm根据条件改变DataGridView行颜色的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要...

    .NET开发菜鸟4472023-02-17
  • C#C#设计模式之策略模式

    C#设计模式之策略模式

    这篇文章介绍了C#设计模式之策略模式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    .NET开发菜鸟4102023-02-17
  • C#C#算法之罗马数字转整数

    C#算法之罗马数字转整数

    本文详细讲解了C#算法之罗马数字转整数,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    痴者工良4062022-12-21
  • C#C#中try...catch的使用与常见面试题分享

    C#中try...catch的使用与常见面试题分享

    这篇文章首先给大家介绍了关于C#中try...catch的语法,而后又给大家分享了关于C#中try...catch最常见的面试题,具有一定的参考借鉴价值,需要的朋友们下面...

    wolfy10902021-12-27