C# 深拷贝和浅拷贝

文章目录

1.深拷贝

拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。

csharp 复制代码
int source = 123;
// 值类型赋值内部执行深拷贝
int copy = source;
// 对拷贝对象进行赋值不会改变源对象的值
copy = 234;
Console.WriteLine($"source = {source},copy = {copy}");  // 123 234
// 同样对源对象赋值也不会改变拷贝对象的值
source = 345;
Console.WriteLine($"source = {source},copy = {copy}");  // 345 234

深拷贝示意图:

2.浅拷贝

==拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。==此时,其中一个对象的改变都会影响到另一个对象。

csharp 复制代码
public class Person
{
    public string Name { get; set; }
}

public static void Main(string[] args)
{
    Person sourceP = new Person() { Name = "张三" };
    Person copyP = sourceP; // 浅拷贝
    copyP.Name = "张老三";  // 拷贝对象改变Name值,源对象Name值也会改变
    Console.WriteLine("Person.Name: [SourceP: {0}] [CopyP:{1}]", sourceP.Name, copyP.Name); // 张老三 张老三
}

浅拷贝示意图:

3.拷贝类

csharp 复制代码
//年级
public class Grade
{
    public int Stu_Grade { get; set; }
}

//学生
public class Student
{
    public string Name { get; set; }

    public int Age { get; set; }

    public Grade Grade { get; set; }

    // 调用Object的MemberwiseClone方法实现浅拷贝
    public Student ShallowCopy()
    {
        return (Student)this.MemberwiseClone();
    }

    // 输出学生信息
    public override string ToString()
    {
        return $"姓名:{Name},年龄:{Age},年纪:{Grade.Stu_Grade}";
    }
}

4.浅拷贝的实现

浅拷贝的实现是调用受保护的方法Object.MemberwiseClone()

csharp 复制代码
public static void Main(string[] args)
{
    Student sourceStu = new Student
    {
        Name = "ming",
        Age = 18,
        Grade = new Grade{ Stu_Grade = 6 }
    };

    Student copyStu = sourceStu.ShallowCopy();

    // 输出修改信息前,源学生和拷贝学生信息
    Console.WriteLine("------修改前------");
    Console.WriteLine("源学生信息" + sourceStu.ToString());
    Console.WriteLine("拷贝信息" + copyStu.ToString());

    copyStu.Name = "Tom";
    copyStu.Age = 26;
    copyStu.Grade.Stu_Grade = 10;

    // 输出修改信息后,源学生和拷贝学生信息
    Console.WriteLine("------修改后------");
    Console.WriteLine("源学生信息" + sourceStu.ToString());
    Console.WriteLine("拷贝信息" + copyStu.ToString());
}

输出:

------修改前------

源学生信息姓名:ming,年龄:18,年级:6

拷贝信息姓名:ming,年龄:18,年级:6

------修改后------

源学生信息姓名:ming,年龄:18,年级:10

拷贝信息姓名:Tom,年龄:26,年级:10

5.深拷贝实现

5.1 浅拷贝对象,对引用类型重新一个个赋值

csharp 复制代码
public Student DeepCopy()
{
    Student student = (Student)this.MemberwiseClone();
    student.Grade = new Grade{ Stu_Grade = student.Grade.Stu_Grade };
    return student;
}

采用和浅拷贝一样的测试数据进行测试,输出:

------修改前------

源学生信息姓名:ming,年龄:18,年级:6

拷贝信息姓名:ming,年龄:18,年级:6

------修改后------

源学生信息姓名:ming,年龄:18,年级:6

拷贝信息姓名:Tom,年龄:26,年级:10

5.2 反射实现

拷贝对象:

csharp 复制代码
//年级
public class Grade
{
    public int Stu_Grade { get; set; }
}

// 成绩
public class Score
{
    public Science Science { get; set; }
    public Arts Arts { get; set; }
}

// 理科
public class Science
{
    // 数学
    public int Math { get; set; }
}

// 文科
public class Arts
{
    // 语文
    public int Chinese { get; set; }
}

//学生
public class Student
{
    public string Name { get; set; }

    public int Age { get; set; }

    public Grade Grade { get; set; }

    public Score Scroe { get; set; }
}

深拷贝实现:

csharp 复制代码
public class DeepCopyHelper
{
    // 反射实现深拷贝
    public static T DeepCopyByReflect<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, DeepCopyByReflect(field.GetValue(obj))); }
            catch { }
        }
        return (T)retval;
    }
}

测试:

csharp 复制代码
public static void Main(string[] args)
{
    Student sourceStu = new Student
    {
        Name = "ming",
        Age = 18,
        Grade = new Grade{ Stu_Grade = 6 },
        Scroe = new Score{ Arts = new Arts{Chinese = 90}, Science = new Science{Math = 90}}
    };

    Student copyStu = DeepCopyHelper.DeepCopyByReflect<Student>(sourceStu);

    // 输出修改信息前,源学生和拷贝学生信息
    Console.WriteLine("------修改前------");
    Console.WriteLine("源学生信息" + sourceStu.ToString());
    Console.WriteLine("拷贝信息" + copyStu.ToString());

    copyStu.Name = "Tom";
    copyStu.Age = 26;
    copyStu.Grade.Stu_Grade = 10;
    copyStu.Scroe.Arts.Chinese = 100;
    copyStu.Scroe.Science.Math = 100;

    // 输出修改信息后,源学生和拷贝学生信息
    Console.WriteLine("------修改后------");
    Console.WriteLine("源学生信息" + sourceStu.ToString());
    Console.WriteLine("拷贝信息" + copyStu.ToString());
}

结果:

------修改前------

源学生信息姓名:ming,年龄:18,年级:6, 成绩:(语文:90,数学:90)

拷贝信息姓名:ming,年龄:18,年级:6, 成绩:(语文:90,数学:90)

------修改后------

源学生信息姓名:ming,年龄:18,年级:6, 成绩:(语文:90,数学:90)

拷贝信息姓名:Tom,年龄:26,年级:10, 成绩:(语文:100,数学:100)

5.3 利用XML序列化和反序列化实现

测试内容和结果和5.2一样

csharp 复制代码
// xml 实现深拷贝
public static T DeepCopyByXml<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;
}
相关推荐
可乐鸡翅yeah_4 小时前
hls.js 缓冲区参数 maxBufferLength、maxBufferSize 通俗讲解与业务调参
开发语言·javascript·ios·音视频·safari·m3u8
Wang's Blog4 小时前
Java框架 SpringCloud 快速入门: 全局过滤器(GlobalFilter)自定义鉴权
java·开发语言·spring cloud
白山编程大哥4 小时前
C语言篇:可变参数函数
c语言·开发语言
(Charon)4 小时前
【C++面试】程序崩溃如何定位:从Segmentation fault到GDB、Core Dump与ASan
开发语言·c++
paopaokaka_luck4 小时前
高校社团管理(AI辅助任务分配、协同过滤算法推荐、ECharts数据可视化、活动参与闭环、校园交流与反馈、器材借还管理)
java·前端·javascript·spring boot·数据分析·echarts
“AI国潮设计-小江”4 小时前
《Python+SDXL实战:用ControlNet精准控制“英歌舞戚风蛋糕”质感,附批量生成脚本》
开发语言·人工智能·python·prompt·aigc
1024奇点4 小时前
【仓颉语言入门 · 第26课】
开发语言·ide·开源
jayhgq4 小时前
新一代Python包与项目管理工具——UV
开发语言·python·uv
xixiaoyunya4 小时前
CPU 100% 本身不是问题,它是一个症状
开发语言·php
vx_Biye_Design4 小时前
springboot小区管理系统68491-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·课程设计·express