.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# char类型字符转换大小写的实现代码

    下面是详细的讲解“C# char类型字符转换大小写的实现代码”的完整攻略。 问题说明 在 C#中,char 类型表示一个 Unicode 字符。有时候我们需要将字符转换为大写或小写形式。C# 语言提供了相应的方法供我们实现。 解决方案 C# 中,char 类型本身包含了 ToUpper 和 ToLower 两个方法,分别用于将字符转换为大写和小写形式。使用这…

    C# 2023年6月1日
    00
  • C# Linq的OrderByDescending()方法 – 根据指定的键按降序对序列的元素进行排序

    当使用C#Linq时,我们常常需要在对数据进行排序时使用OrderByDescending()方法。这个方法可以根据指定的条件对数据进行降序排序。 下面是使用OrderByDescending()方法的完整攻略: 格式 var result = collection.OrderByDescending(item => item.Property); 其…

    C# 2023年4月19日
    00
  • c#的异或运算符介绍

    C#的异或运算符介绍 什么是异或运算符? 异或运算符是一种二进制运算符,用符号 ^ 表示。它的作用是将两个二进制数的每一位进行比较,如果相应位相同则结果为0,否则结果为1。 具体地,异或运算符有以下规则: a b a^b 0 0 0 0 1 1 1 0 1 1 1 0 如何在C#中使用异或运算符? 在C#中,异或运算符可以用于整型、长整型、短整型等基本数据类…

    C# 2023年6月7日
    00
  • C# Linq的OfType()方法 – 返回序列中指定类型的元素

    下面是C# Linq的OfType()方法的详细讲解: C# Linq OfType() 方法的作用 C# Linq OfType() 方法是用于从集合中筛选出具有指定类型的元素的方法。在某些情况下,我们需要从一个集合中仅仅保留某一种类型的元素,这时我们就可以使用 OfType() 方法。 OfType() 方法可以作用于任何实现了 IEnumerable(…

    C# 2023年4月19日
    00
  • abp(net core)+easyui+efcore实现仓储管理系统——组织管理升级之下(六十二)

    Abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+easyui+efcore实现仓储管理系统——解决方案介绍(二) abp(net core)+easyui+efcore实现仓储管理系统——领域层创建实体(三)…

    C# 2023年4月27日
    00
  • C#实现大数字运算的实例代码

    C#实现大数字运算的实例代码攻略 什么是大数字运算 大数字运算是指对于超过计算机所能直接表示的数字,可以通过算法实现运算。在C#中,数字类型有限,当数字过大时,计算结果可能会溢出或者得出错误的结果。为了解决这种问题,需要用大数字运算方式来处理。 C#中的大数字运算 C#中提供了BigInteger结构和BigDecimal类,可以用于大数字运算。在进行大数字…

    C# 2023年6月7日
    00
  • npoi2.0将datatable对象转换为excel2007示例

    针对将DataTable对象转换为Excel2007的问题,可以使用NPOI来实现。步骤分为以下几个:首先创建一个Workbook对象;接着创建一个Sheet对象,指定Sheet名称;然后创建表头header;接着将header插入到Excel表格的第一行中;最后将DataTable中的数据逐行写入Excel表格中。 下面给出两个具体的示例说明: 示例一 u…

    C# 2023年6月8日
    00
  • C#中for循环、while循环循环执行的方法

    C#中的for循环和while循环是常用的循环结构,用于重复执行相同或类似的代码块,下面是它们的详细讲解和示例说明: for循环 for循环是一种经典的循环语句,用于重复执行一段代码,可以控制循环变量的初始值、终止条件和每次循环变量的增量。for循环的语法如下: for (初始化表达式; 循环条件; 迭代语句) { // 循环体语句 } 其中,初始化表达式只…

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