浅拷贝与深拷贝的区别

发布时间 2023-11-30 22:17:26作者: HackerVirus

C#中的浅拷贝与深拷贝

 

前言

众所周知,C#中有两种类型变量:那就是值类型引用类型。对于值类型而言,copy就相当于是全盘复制了,真正的实现了复制,属于深拷贝;而对于引用类型而言,一般的copy只是浅拷贝,只是copy到了引用对象的地址,相当于值传递了一个引用指针,新的对象通过地址引用仍然指向原有内存中的对象。

什么是浅拷贝和深拷贝

​ 浅拷贝:只复制对象的基本类型、对象类型、仍然属于原引用,也称为影子克隆

image

​ 深拷贝:不止复制对象的基本类,同时也复制原对象中的对象,完全就是新对象产生的,也称为深度克隆

image

浅拷贝的实现

C#中System.Object 是所有类类型、结构类型、枚举类型和委托类型的基类,可以说它是类型继承的基础。System.Object包括一个用于创建当前对象实例的一份拷贝的MemberwiseClone的成员方法。MemberwiseClone方法创建一个新对象的浅拷贝,并把当前对象实例的非静态字段拷贝至新对象实例中。通过属性,对象拷贝能够正确执行:如果属性是值类型,那么将按位拷贝数据,如果属性是引用类型,那么将拷贝原始对象的引用,也就是说,克隆对象指向同一个对象实例。这就意味着MemberwiseClone方法并未创建一个对象的深拷贝。

Object成员方法MemberwiseClone实现:

/// <summary>
    /// Student 学生类
    /// </summary>
    public class Student
    {
        /// <summary>
        /// 姓名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 年龄
        /// </summary>
        public int Age { get; set; }
        /// <summary>
        /// 教室
        /// </summary>
        public  ClassRoom Room { get; set; }

        /// <summary>
        /// 浅拷贝克隆方法
        /// </summary>
        /// <returns></returns>
        public Student Clone()
        {
            return (Student)this.MemberwiseClone();
        }

    }
    /// <summary>
    /// 班级类
    /// </summary>
    public class ClassRoom
    {
        /// <summary>
        /// 班级Id
        /// </summary>
        public int Id { get; set; } 

        /// <summary>
        /// 班级名称
        /// </summary>
        public string Name { get; set; }
        
    }

深拷贝的实现

深拷贝是指复制对象的所有数据,包括对象内部引用的其他对象。这意味着,新的对象和原始对象不共享任何数据,它们是完全独立的。可通过提供如下四种方法实现:

利用反射实现:

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 | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
  foreach (FieldInfo field in fields)
  {
    try { field.SetValue(retval, DeepCopy(field.GetValue(obj))); }
    catch { }
  }
  return (T)retval;
}

利用xml序列化和反序列化实现:

public T DeepCopy<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 DeepCopy<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;
}

DataContractSerializer实现:

public static T DeepCopy<T>(T obj)
    {
      object retval;
      using (MemoryStream ms = new MemoryStream())
      {
        DataContractSerializer ser = new DataContractSerializer(typeof(T));
        ser.WriteObject(ms, obj);
        ms.Seek(0, SeekOrigin.Begin);
        retval = ser.ReadObject(ms);
        ms.Close();
      }
      return (T)retval;

Newtonsoft.Json.JsonConvert实现:

 public static T DeepCopy<T>(T obj)
        {
            if (obj == null)
                return default(T);
            T object2 = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(Newtonsoft.Json.JsonConvert.SerializeObject(obj));

            return object2;
        }

浅拷贝与深拷贝的区别

​ 浅拷贝:是指将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用类型的字段则指复制它的一个引用到目标对象。如果改变目标对象中引用型字段的值它将反应在原始对象中,也就是说原始对象中对应的字段也会发生变化。

深拷贝:与浅拷贝的不同是对于引用的处理,深拷贝将会在内存中创建一个新的对象,对应字段和原始对象完全相同。也就是说这个引用和原始对象的引用是不相同的,因为新对象与原对象引用地址不同,我们在改变新对象中的这个字段的时候是不会影响到原始字段中对应字段的内容。

本文来自博客园,作者:码农阿亮,转载请注明原文链接:https://www.cnblogs.com/wml-it/p/17667229.html


技术的发展日新月异,随着时间推移,无法保证本博客所有内容的正确性。如有误导,请大家见谅,欢迎评论区指正!
开源库地址,欢迎点亮:
GitHub:https://github.com/ITMingliang
Gitee:   https://gitee.com/mingliang_it
GitLab: https://gitlab.com/ITMingliang

建群声明: 本着技术在于分享,方便大家交流学习的初心,特此建立【编程内功修炼交流群】,为大家答疑解惑。热烈欢迎各位爱交流学习的程序员进群,也希望进群的大佬能不吝分享自己遇到的技术问题和学习心得!进群方式:扫码关注公众号,后台回复【进群】。