C#深拷贝方法探究及性能比较(多种深拷贝)

C#深拷贝方法探究及性能比较(多种深拷贝)

什么是深拷贝

在 C# 的开发中,经常需要复制一份对象,以便于对该对象进行修改而不影响原来的对象。浅拷贝(shallow copy)只是简单地复制了一份对象的引用,而不是实际的对象,原始对象和副本对象共享引用类型的成员变量。而深拷贝(deep copy)则会创建一份新的对象,并复制原始对象所有的成员变量,包括引用类型成员变量所引用的对象。

深拷贝的实现方式

1.手动实现

手动实现深拷贝的方法是通过递归复制所有被引用的对象及其属性。我们可以按照如下格式实现一个对象的深拷贝:

public class MyClass
{
    public int IntProperty { get; set; }
    public MySecondClass MySecondClassProperty { get; set; }

    public MyClass DeepCopy()
    {
        MyClass copy = new MyClass();
        copy.IntProperty = this.IntProperty;
        copy.MySecondClassProperty = new MySecondClass();
        copy.MySecondClassProperty.Property1 = this.MySecondClassProperty.Property1;
        copy.MySecondClassProperty.Property2 = this.MySecondClassProperty.Property2;
        copy.MySecondClassProperty.Property3 = this.MySecondClassProperty.Property3;
        return copy;
    }
}

public class MySecondClass
{
    public int Property1 { get; set; }
    public string Property2 { get; set; }
    public bool Property3 { get; set; }
}

虽然手动实现深拷贝比较繁琐,但是可以确保100%的拷贝结果正确,且运行速度比其他方法快一些。

2.使用序列化实现深拷贝

使用序列化实现深拷贝的方法是对被拷贝对象进行序列化和反序列化,实现对象的克隆。以下是使用BinaryFormatter实现对象的深拷贝的示例代码:

public static T DeepCopy<T>(T obj)
{
    using var stream = new MemoryStream();
    var formatter = new BinaryFormatter
    {
        Context = new StreamingContext(StreamingContextStates.Clone)
    };
    formatter.Serialize(stream, obj);
    stream.Seek(0, SeekOrigin.Begin);
    return (T)formatter.Deserialize(stream);
}

但是,使用序列化实现深拷贝的方法会对性能有一定的影响,并且不是所有对象都可以序列化。

3.使用反射实现深拷贝

使用反射实现深拷贝的方法是通过反射机制获取对象的类型信息,然后对其进行复制的操作。以下是使用反射实现对象的深拷贝的示例代码:

public static T DeepCopy<T>(T obj)
{
    if (obj == null)
    {
        return default(T);
    }

    var type = obj.GetType();
    if (!type.IsValueType && type != typeof(string))
    {
        object copy = Activator.CreateInstance(obj.GetType());
        foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
        {
            var fieldValue = field.GetValue(obj);
            if (fieldValue != null)
            {
                field.SetValue(copy, DeepCopy(fieldValue));
            }
        }

        foreach (PropertyInfo property in type.GetProperties())
        {
            if (property.CanRead && property.CanWrite && property.GetIndexParameters().Length == 0)
            {
                try
                {
                    var propertyValue = property.GetValue(obj, null);
                    if (propertyValue != null)
                    {
                        property.SetValue(copy, DeepCopy(propertyValue), null);
                    }
                }
                catch
                {
                    // Some indexers or properties may throw exception here, but I don’t care.
                }
            }
        }

        obj = (T)copy;
    }

    return obj;
}

深拷贝的性能比较

我们可以使用以下代码对三种深拷贝方法的性能进行比较:

var sourceObject = CreateLargeObjectGraph();
var stopwatch = Stopwatch.StartNew();

var copyByReflection = DeepCopyByReflection(sourceObject);
var reflectionTime = stopwatch.Elapsed;

stopwatch.Restart();

var copyBySerialization = DeepCopyBySerialization(sourceObject);
var serializationTime = stopwatch.Elapsed;

stopwatch.Restart();

var copyByManual = sourceObject.DeepCopy();
var manualTime = stopwatch.Elapsed;

其中,CreateLargeObjectGraph()方法用于创建一个包含1K个MyClass对象的对象图。

实验结果表明,手动实现深拷贝方法的速度最快,而基于序列化实现深拷贝的方法速度最慢。

示例说明

下面我们以一个Student类为例,演示如何使用各种深拷贝方法进行对象的拷贝。

public class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    public List<string> Courses { get; set; }

    public Student DeepCopy()
    {
        var copy = new Student();
        copy.Name = this.Name;
        copy.Age = this.Age;
        copy.Courses = new List<string>();
        foreach(var course in this.Courses)
        {
            copy.Courses.Add(course);
        }
        return copy;
    }
}

使用手动实现深拷贝方法:

var student1 = new Student() { Name = "Tom", Age = 18, Courses = new List<string>() { "Math", "English" } };
var student2 = student1.DeepCopy();
student1.Name = "Jerry";
student1.Age = 19;
student1.Courses.Add("History");
Console.WriteLine(student1.Name + " " + student1.Age + " " + string.Join(",", student1.Courses));
Console.WriteLine(student2.Name + " " + student2.Age + " " + string.Join(",", student2.Courses));

运行以上代码,输出:

Jerry 19 Math,English,History
Tom 18 Math,English

使用BinaryFormatter实现深拷贝方法:

var student1 = new Student() { Name = "Tom", Age = 18, Courses = new List<string>() { "Math", "English" } };
var student2 = DeepCopyBySerialization(student1);
student1.Name = "Jerry";
student1.Age = 19;
student1.Courses.Add("History");
Console.WriteLine(student1.Name + " " + student1.Age + " " + string.Join(",", student1.Courses));
Console.WriteLine(student2.Name + " " + student2.Age + " " + string.Join(",", student2.Courses));

使用反射实现深拷贝方法:

var student1 = new Student() { Name = "Tom", Age = 18, Courses = new List<string>() { "Math", "English" } };
var student2 = DeepCopyByReflection(student1);
student1.Name = "Jerry";
student1.Age = 19;
student1.Courses.Add("History");
Console.WriteLine(student1.Name + " " + student1.Age + " " + string.Join(",", student1.Courses));
Console.WriteLine(student2.Name + " " + student2.Age + " " + string.Join(",", student2.Courses));

以上这些示例可以帮助我们理解三种深拷贝方法的实现过程,并在实际开发中提供参考。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#深拷贝方法探究及性能比较(多种深拷贝) - Python技术站

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

相关文章

  • 答你所问 .NET小常识 方便学习asp.net的朋友

    让我为您详细讲解“答你所问 .NET小常识 方便学习asp.net的朋友”的完整攻略。 一、什么是.NET? .NET是由微软公司开发的一个软件框架,目的是为了开发Windows操作系统、Web 应用程序、移动设备应用程序等基于Windows平台的应用程序提供一个强大的编程支持环境。.NET框架包括一个大型库,所有这些库都使用 C#、VB.NET、C++ 等…

    C# 2023年5月31日
    00
  • C#中Backgroundworker与Thread的区别

    在C#中,可以使用BackgroundWorker和Thread类来实现多线程编程。这两个类最初的目的都是为了实现异步操作,但它们使用的机制和易用性却有着较大的不同。 BackgroundWorker与Thread的区别 机制 BackgroundWorker类是基于事件的异步操作机制,它将异步操作分为三个阶段:DoWork、ProgressChanged、…

    C# 2023年5月15日
    00
  • C#设计模式之Template模板方法模式实现ASP.NET自定义控件 密码强度检测功能

    C#设计模式之Template模板方法模式实现ASP.NET自定义控件密码强度检测功能 目的 本文介绍如何通过使用C#设计模式中的Template模式,实现ASP.NET自定义控件中的密码强度检测功能。 前提条件 本文假设读者已经具备以下知识储备: C#编程语言基础 ASP.NET自定义控件的基础知识 设计模式中的Template模式基础概念和使用方法 实现…

    C# 2023年6月3日
    00
  • 浅谈c#中const与readonly区别

    浅谈C#中const与readonly区别 在C#编程中,常量(constant)和只读字段(readonly field)是两种常见的实现常量的方式。但是这两种方式有着不同的使用场景和限制。本文将详细讲解C#中const和readonly的区别及其使用方法。 const常量 const关键字用于定义编译时常量,必须在定义时进行初始化,并且初始化的值不能被修…

    C# 2023年6月7日
    00
  • 详解C#中SqlParameter的作用与用法

    详解C#中SqlParameter的作用与用法 在C#中,SqlParameter是用于向SQL Server数据库发送参数化查询的类。它可以帮助我们避免SQL注入攻击,并提高查询性能。本文将提供详细的“详解C#中SqlParameter的作用与用法”的完整攻略,包括SqlParameter的作用、SqlParameter的用法以及两个示例。 SqlPara…

    C# 2023年5月15日
    00
  • Unity ScrollRect实现轨迹滑动效果

    首先介绍一下Unity ScrollRect。 ScrollRect是Unity中ScrollView 的组件之一。这个组件提供了一个类似于滚动列表的UI组件,可通过拖动或操作滚动条滚动内容。 接下来详细讲解Unity ScrollRect实现轨迹滑动效果的完整攻略: 创建新项目并新建Canvas对象 在Canvas对象下创建一个新的Panel,将Panel…

    C# 2023年6月3日
    00
  • C#延迟执行方法函数实例讲解

    C#延迟执行方法函数实例讲解 什么是延迟执行 延迟执行是指在需要的时候才会进行真正的计算或执行,它可以提高程序的执行效率,在一些需要消耗大量资源或时间的情况下尤为重要。 C#中的延迟执行 C#中延迟执行可以通过Lambda表达式、Func和Action委托等方式实现。 Lambda表达式实现延迟执行 Lambda表达式是一种简单、紧凑的语法形式,可以在需要的…

    C# 2023年6月1日
    00
  • 字符串阵列String[]转换为整型阵列Int[]的实例

    将字符串数组String[]转换为整型数组int[]是编程中很常见的操作,我们可以使用Java提供的内置函数进行转换。 以下是转换的完整攻略: 1.遍历字符串数组 首先,我们需要遍历字符串数组String[],并且将每个元素转换为整型。 String[] strArray = {"10", "20", "30…

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