.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日

相关文章

  • MySQL中Decimal类型和Float Double的区别(详解)

    MySQL中Decimal类型和Float Double的区别(详解) Decimal类型 Decimal类型是MySQL中用于表示精确小数的一种数据类型,它可以精确地表示较小范围内的小数,如货币等领域。Decimal类型常用于财务计算、税收计算等需要精确小数计算的场景中。 Decimal类型的存储范围为1到65位,其中1到30位用于存储小数部分,而整数部分…

    C# 2023年5月31日
    00
  • 解析C#中的ref和out参数

    解析C#中的ref和out参数 C#中的ref和out参数都是用来传递参数的,它们可以让方法修改调用时传递的参数,而不是传递参数的副本,这节省了复制大量数据的开销,同时也可以避免数据错误。 ref参数 ref参数表示传递的是实参的引用(地址),方法可以直接修改这个引用所指向的变量的值。ref参数会把实参的引用(地址)传递给方法,然后方法会直接用这个引用(地址…

    C# 2023年6月7日
    00
  • ASP.NET操作各类时间段获取方法汇总

    ASP.NET操作各类时间段获取方法汇总 在ASP.NET中,我们常常需要获取各类时间段,例如获取当前时间、获取某个日期的年月日信息、获取指定时间段的日期列表。本文将系统介绍ASP.NET操作各类时间段获取方法及其使用场景,包括以下几个方面: 获取当前时间 获取当前日期的年月日信息 获取指定时间段的日期列表 1. 获取当前时间 要获取当前时间,我们可以使用 …

    C# 2023年6月1日
    00
  • .net msmq消息队列实例详解

    .NET MSMQ消息队列实例详解 在.NET开发中,消息队列是一种常见的通信机制,可以实现异步通信、解耦和可靠性等功能。本文将介绍.NET中的消息队列实现MSMQ(Microsoft Message Queuing)的详细使用方法。 安装MSMQ 在使用MSMQ之前,需要先安装MSMQ组件。在Windows操作系统中,可以通过以下步骤安装MSMQ: 打开“…

    C# 2023年5月15日
    00
  • 探讨如何配置SQL2008,让其允许C#远程外部连接的方法详解

    探讨如何配置SQL2008,让其允许C#远程外部连接的方法: 配置SQL2008的网络设置 打开”SQL Server Configuration Manager”,选择 “SQL Server Network Configuration”,然后选择SQL Server服务的名称。 选择 “TCP/IP” 选项卡。 若TCP/IP未启用,则右键选择 “TCP…

    C# 2023年6月2日
    00
  • C#实现把图片转换成二进制以及把二进制转换成图片的方法示例

    下面是详细的C#实现把图片转换成二进制以及把二进制转换成图片的方法示例攻略: 把图片转换成二进制 方法一:使用FileStream 首先需要用 FileStream 类打开图片,读取图片的二进制数据: string imagePath = "image.jpg"; byte[] imageData; using (var stream =…

    C# 2023年5月31日
    00
  • 使用C#调用系统API实现内存注入的代码

    使用C#调用系统API实现内存注入需要遵循以下步骤: 获取目标进程ID 使用系统API函数Process.GetProcessesByName(string processName)可以获取指定名称进程的所有进程实例,然后通过进程实例的Id属性获取目标进程ID。 Process[] processes = Process.GetProcessesByName…

    C# 2023年5月31日
    00
  • c#实现汉诺塔问题示例

    C#实现汉诺塔问题示例 汉诺塔问题是经典的数学问题之一,其规则如下: 有三根针,上面从上到下按小到大顺序套着圆盘,现在要把圆盘从其中一个针移到另一个针上。每次只能移动一个圆盘,且大的圆盘不能放在小的圆盘之上。问如何操作。 解题思路 汉诺塔问题和递归算法有着紧密联系,因此我们可以利用递归算法来解决汉诺塔问题。 设有a、b、c三个针,将n个盘子从a针移到b针: …

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