C#中的浅拷贝与深拷贝

c#,拷贝 · 浏览次数 : 1045

小编点评

**前言** C#中有两种类型变量:那就是值类型和引用类型。对于值类型而言,copy就相当于是全盘复制了,真正的实现了复制,属于深拷贝;而对于引用类型而言,一般的copy只是浅拷贝,只是copy到了引用对象的地址,相当于值传递了一个引用指针,新的对象通过地址引用仍然指向原有内存中的对象。 **浅拷贝** 浅拷贝只复制对象的基本类型、对象类型、仍然属于原引用,也称为影子克隆。 * **实现**:利用 `MemberwiseClone` 方法创建浅拷贝。 * **优点**:简单易实现,性能较低。 * **缺点**:无法复制引用类型字段,如果改变目标对象中引用型字段的值它将反应在原始对象中,也就是说原始对象中对应的字段也会发生变化。 **深拷贝** 深拷贝是指复制对象的所有数据,包括对象内部引用的其他对象。这意味着,新的对象和原始对象不共享任何数据,它们是完全独立的。 * **实现**:利用反射实现,利用xml序列化和反序列化实现,或者利用二进制序列化和反序列化实现。 * **优点**:可以完全复制对象,包括引用类型字段。 * **缺点**:性能较高,复杂性较高。 **浅拷贝与深拷贝的区别** | 特征 | 浅拷贝 | 深拷贝 | |---|---|---| | 字段类型 | 基础类型、对象类型、引用类型 | 引用类型 | | 复制过程 | 将对象中的数值类型的字段拷贝到新的对象中 | 将对象中的引用类型的字段复制到新的对象中 | | 对引用类型字段的处理 | 复制该字段的引用,并将其指向同一个内存地址 | 复制该字段的值,并将其指向不同的内存地址 | | 对目标对象的性能影响 | 降低 | 提高 |

正文

前言

众所周知,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;
        }

浅拷贝与深拷贝的区别

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

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

与C#中的浅拷贝与深拷贝相似的内容:

C#中的浅拷贝与深拷贝

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

C#中的对象深拷贝和浅拷贝

目录C#中的对象深拷贝和浅拷贝概述1. 浅拷贝2. 深拷贝总结引用 C#中的对象深拷贝和浅拷贝 概述 在C#中,对象拷贝是指将一个对象的副本创建到另一个对象中。对象拷贝通常用于数据传输或创建对象的新实例。 C#中有两种主要的拷贝方式:浅拷贝和深拷贝 1. 浅拷贝 浅拷贝是指只拷贝对象的值类型成员,而

C++ 中的 lowbit

lowbit 的定义 首先了解 lowbit 的定义 \(lowbit(n)\) ,为 \(n\) 的二进制原码中最低的一位 \(1\) 以及其后面的 \(0\) 所表示的数 举个简单的例子: 将 \(10\) 使用二进制表示为 \(1010\) 其中最低位的 \(1\) 为第2位(\(_{10}1

toLua中Lua调用C#中的类

toLua中Lua调用C#: [7]Lua脚本调用C#中的class 准备工作:打算在Lua脚本中使用Debug,使用lua调用C#脚本,需要绑定LuaState和自定义添加Debug Generated by EmmyLua(https://github.com/EmmyLua) Created

深入理解 C++ 中的多态与文件操作

C++ 多态 多态(Polymorphism)是面向对象编程(OOP)的核心概念之一,它允许对象在相同操作下表现出不同的行为。在 C++ 中,多态通常通过继承和虚函数来实现。 理解多态 想象一个场景,你有一个动物园,里面有各种动物,如猫、狗、鸟等。每个动物都有自己的叫声。使用面向对象编程,我们可以创

C++的extern关键字在HotSpot VM中的重要应用

extern关键字有两个用处: (1)extern在C/C++语言中表示函数和全局变量作用范围(可见性)的关键字,这个关键字会告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。 (2)在C++中引用C语言中的函数和变量,在包含C语言头文件时,需要使用extern "C"来处理。 1、ext

热更学习笔记10~11----lua调用C#中的List和Dictionary、拓展类中的方法

[10]Lua脚本调用C#中的List和Dictionary 调用还是在上文中使用的C#脚本中Student类: lua脚本: print(" 访问使用C#脚本中的List和Dictionary ") student.list:Add(2024) student.list:Add(5) studen

热更学习笔记--toLau中lua脚本对C#中枚举和数组的访问

[8]Lua脚本调用C#中的枚举学习 --调用枚举类型 print(" toLua中调用C#中枚举类型 ") PrimitiveType = UnityEngine.PrimitiveType local cubeObj = GameObject.CreatePrimitive(PrimitiveT

C++ 重载运算符在HotSpot VM中的应用

C++支持运算符重载,对于Java开发者来说,这个可能比较陌生一些,因为Java不支持运算符重载。运算符重载本质上来说就是函数重载。下面介绍一下HotSpot VM中的运算符重载。 1、内存分配与释放 在C++中可以通过new运算符创建一个C++的类实例,这个操作实际上上包含了如下3个步骤: 调用o

QML 怎么调用 C++ 中的内容?

关于Qt Quick里的开发笔记,这里主要是总结一下,怎么在 QML 文件中引用 C ++ 文件里定义的内容。