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;
}
相关推荐
苦夏木禾14 分钟前
js请求避免缓存的三种方式
开发语言·javascript·缓存
重庆小透明19 分钟前
力扣刷题记录【1】146.LRU缓存
java·后端·学习·算法·leetcode·缓存
超级土豆粉23 分钟前
Turndown.js: 优雅地将 HTML 转换为 Markdown
开发语言·javascript·html
lang2015092825 分钟前
Reactor操作符的共享与复用
java
TTc_35 分钟前
@Transactional事务注解的批量回滚机制
java·事务
金增辉1 小时前
基于C#的OPCServer应用开发,引用WtOPCSvr.dll
c#
wei_shuo1 小时前
飞算 JavaAI 开发助手:深度学习驱动下的 Java 全链路智能开发新范式
java·开发语言·飞算javaai
熊猫钓鱼>_>1 小时前
用Python解锁图像处理之力:从基础到智能应用的深度探索
开发语言·图像处理·python
GO兔2 小时前
开篇:GORM入门——Go语言的ORM王者
开发语言·后端·golang·go
欧阳秦穆2 小时前
apoc-5.24.0-extended.jar 和 apoc-4.4.0.36-all.jar 啥区别
java·jar