.NET的深复制方法(以C#语言为例)

以下是关于.NET的深复制(Deep copy)的说明和实例:

深复制的概念

在.NET中,对象类型包括值类型和引用类型。值类型在进行复制时,会重新创建一个新的副本;而引用类型在进行复制时,复制的只是引用地址,指向同一个原始对象。这样可能会引起一些问题,例如,当我们修改复制后的对象时,会影响原始对象。为了避免这种问题,我们需要进行深复制,即复制整个对象及其引用类型的所有子对象。所以深复制后的对象和原始对象是完全独立的,对复制对象的任何修改都不会影响原始对象。

深复制的两种常见实现方式

1.使用序列化和反序列化

使用这种方法,我们需要将需要复制的对象序列化并反序列化得到一个新的对象。其中,序列化将对象转化成字节流,而反序列化则将这些字节流还原成对象。示例如下:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

// 学生类
[Serializable]
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

// 地址类
[Serializable]
public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var student1 = new Student
        {
            Id = 1,
            Name = "小明",
            Address = new Address
            {
                City = "北京",
                Street = "xx 街道"
            }
        };

        // 深复制
        var student2 = SerializeCopy<Student>(student1);

        Console.WriteLine($"student1: {student1.Name}, {student1.Address.City}, {student1.Address.Street}");
        Console.WriteLine($"student2: {student2.Name}, {student2.Address.City}, {student2.Address.Street}");

        // 修改student2
        student2.Name = "小红";
        student2.Address.City = "上海";

        Console.WriteLine($"修改后的student2: {student2.Name}, {student2.Address.City}, {student2.Address.Street}");
        Console.WriteLine($"原始的student1: {student1.Name}, {student1.Address.City}, {student1.Address.Street}");
        Console.ReadKey();
    }

    // 使用序列化和反序列化进行深复制
    public static T SerializeCopy<T>(T obj)
    {
        using (var memory = new MemoryStream())
        {
            var formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));

            formatter.Serialize(memory, obj);

            memory.Position = 0;

            return (T)formatter.Deserialize(memory);
        }
    }
}

上述代码中,我们首先定义了一个StudentAddress类,然后对student1对象进行了初始化。接着,我们调用SerializeCopy方法进行深复制,得到了一个完整独立的student2对象。然后,我们输出student1student2的属性值,可以看到它们的属性值完全相同,但这并不代表它们是同一个对象。接着,我们演示了修改student2后会影响原始的student1对象的情况。

2.使用反射

不建议使用这种方式,因为它的效率和性能不如使用序列化方式。但是,这种方式确实比较灵活,可以自定义实现。这种方法需要使用反射获取并创建对象的所有字段和属性,递归遍历复制整个对象。示例如下:

using System;
using System.Reflection;

// 学生类
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

// 地址类
public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var student1 = new Student
        {
            Id = 1,
            Name = "小明",
            Address = new Address
            {
                City = "北京",
                Street = "xx 街道"
            }
        };

        // 深复制
        var student2 = ReflectionCopy<Student>(student1);

        Console.WriteLine($"student1: {student1.Name}, {student1.Address.City}, {student1.Address.Street}");
        Console.WriteLine($"student2: {student2.Name}, {student2.Address.City}, {student2.Address.Street}");

        // 修改student2
        student2.Name = "小红";
        student2.Address.City = "上海";

        Console.WriteLine($"修改后的student2: {student2.Name}, {student2.Address.City}, {student2.Address.Street}");
        Console.WriteLine($"原始的student1: {student1.Name}, {student1.Address.City}, {student1.Address.Street}");
        Console.ReadKey();
    }

    // 使用反射进行深复制
    public static T ReflectionCopy<T>(T obj)
    {
        // 获取类型和构造函数
        var type = obj.GetType();
        var obj2 = Activator.CreateInstance(type);

        // 获取属性和字段
        var fields = type.GetFields();
        var properties = type.GetProperties();

        // 遍历属性和字段,递归设置值
        foreach (var field in fields)
        {
            var fieldName = field.Name;
            var fieldValue = field.GetValue(obj);

            if (fieldValue == null || field.FieldType.IsValueType || field.FieldType == typeof(string))
            {
                field.SetValue(obj2, fieldValue);
            }
            else
            {
                field.SetValue(obj2, ReflectionCopy(fieldValue));
            }
        }

        foreach (var property in properties)
        {
            var propertyName = property.Name;
            var propertyValue = property.GetValue(obj);

            if (propertyValue == null || property.PropertyType.IsValueType || property.PropertyType == typeof(string))
            {
                property.SetValue(obj2, propertyValue);
            }
            else
            {
                property.SetValue(obj2, ReflectionCopy(propertyValue));
            }
        }

        return (T)obj2;
    }
}

上述代码中,我们使用反射获取并复制了整个对象的所有属性和字段。注意,我们使用了递归来处理对象的属性和字段。可以看到,这种方法比使用序列化方式要复杂和低效。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:.NET的深复制方法(以C#语言为例) - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • C#中的随机数函数Random()

    C#中的Random()函数可以用来生成随机数,其基本用法如下: Random random = new Random(); // 创建一个新的Random对象 int randomNumber = random.Next(); // 生成一个随机的32位有符号整数 注意,这里使用了默认的构造函数来创建Random对象,如果需要指定随机数生成器的种子值,可以…

    C# 2023年6月8日
    00
  • 浅谈C#9.0新特性之参数非空检查简化

    首先,C# 9.0中引入的新特性包含了很多实用的语言功能,其中参数非空检查简化就是其中之一。在传统的C#语言中,我们常使用条件判断语句来检查参数是否为null,这样代码可读性较差,而C# 9.0中的新特性可以更加方便快捷地进行参数非空检查。 简化前的参数非空检查 在C# 9.0之前,我们通常使用以下方式来进行参数非空检查: void PrintMessage…

    C# 2023年5月15日
    00
  • 关于C#结构体 你需要知道的

    关于C#结构体 你需要知道的 在C#中,结构体是一种轻量级的数据类型,它是一种值类型,而不是引用类型。结构体可以包含字段、方法、属性、构造函数和操作符等成员。 为什么要使用结构体 使用结构体可以提高程序的性能和效率。因为结构体是值类型,而值类型是直接存储在栈上的,这样就避免了装箱和拆箱带来的性能损失。另外,结构体通常不需要被垃圾回收机制处理,所以也减少了内存…

    C# 2023年5月31日
    00
  • WPF实现Interaction框架的Behavior扩展

    WPF实现Interaction框架的Behavior扩展可以让我们方便地将事件与命令关联起来,使得我们可以在应用程序中使用MVVM模式。本篇攻略将告诉你如何创建Behavior扩展,并提供两个示例。 创建Behavior扩展 Behavior扩展是一个继承自System.Windows.Interactivity.Behavior类的类。定义一个Behav…

    C# 2023年6月3日
    00
  • C#中的递归APS和CPS模式详解

    C#中的递归APS和CPS模式详解 什么是递归APS模式 递归APS(Also Known As All-Pairs Shortest Path)模式是一种计算图中所有顶点之间最短路径的算法。我们可以使用递归APS模式在C#中找到图中所有顶点的最短路径。 在C#中,我们可以使用递归调用来实现递归APS。 递归APS模式的基本思想 递归APS模式可以被看做是动…

    C# 2023年6月7日
    00
  • C# 多线程处理List数据的示例代码

    我们来详细讲解一下“C# 多线程处理List数据的示例代码”的完整攻略吧。 1. 理解多线程处理List数据的必要性 在C#中,当需要处理大量数据时,多线程是提高程序效率的一种好方式,特别是在处理大规模的数据集合时,利用多线程可以缩短程序处理时间,提高程序的执行效率。 2. 实现多线程处理List数据的示例代码 下面我们来看一下实现多线程处理List数据的示…

    C# 2023年6月6日
    00
  • C#微信公众号开发之服务器配置

    C#微信公众号开发之服务器配置 本文主要介绍在使用C#进行微信公众号开发过程中,如何进行服务器配置,以让公众号接收用户消息和事件以及进行回复。下面就是服务器配置的完整攻略: 1. 登录开发者平台 首先,在微信公众平台官网登录自己的开发者账号,然后进入“开发->基本配置”界面,在该界面获取自己的AppID和AppSecret,为后面进行开发提供必要的认证…

    C# 2023年6月6日
    00
  • C#递归实现显示文件夹及所有文件并计算其大小的方法

    下面是“C#递归实现显示文件夹及所有文件并计算其大小的方法”的完整攻略。 1. 确定递归终止条件 首先,我们要确定递归的终止条件。在本题中,终止条件一般是当遍历到文件时,直接输出文件的名称和大小,并返回上一级目录继续遍历。因此,我们可以使用File和Directory类来判定当前路径是否为文件或目录,并在遇到文件时直接输出。 2. 确定遍历方式 其次,我们需…

    C# 2023年6月1日
    00
合作推广
合作推广
分享本页
返回顶部