我想做一些事情,比如:

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();

然后对未反映在原始对象中的新对象进行更改。

我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。

如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?


当前回答

通常,您实现ICloneable接口并自己实现克隆。C#对象有一个内置的MemberwiseColone方法,该方法执行浅层复制,可以帮助您处理所有原语。

对于深度复制,它无法知道如何自动执行。

其他回答

免责声明:我是上述包裹的作者。

我很惊讶2019年这个问题的顶级答案仍然使用序列化或反射。

序列化是有限的(需要属性、特定构造函数等),而且非常慢

BinaryFormatter需要Serializable属性,JsonConverter需要无参数构造函数或属性,既不能很好地处理只读字段或接口,两者都比需要的慢10-30x。

表达式树

您可以改为使用表达式树或Reflection.Emit仅生成一次克隆代码,然后使用编译后的代码,而不是缓慢的反射或序列化。

我自己遇到了这个问题,但没有找到令人满意的解决方案,因此我决定创建一个包,它可以做到这一点,并且可以处理所有类型的代码,而且速度几乎与自定义编写的代码一样快。

您可以在GitHub上找到该项目:https://github.com/marcelltoth/ObjectCloner

用法

您可以从NuGet安装它。获取ObjectCloner包并将其用作:

var clone = ObjectCloner.DeepClone(original);

或者如果您不介意用扩展名污染对象类型,请获取ObjectCloner.extensions,并写下:

var clone = original.DeepClone();

表演

克隆类层次结构的简单基准测试显示,性能比使用Reflection快约3倍,比Newtonsoft.Json序列化快约12倍,比强烈建议的BinaryFormatter快约36倍。

这将把一个对象的所有可读写财产复制到另一个对象。

 public class PropertyCopy<TSource, TTarget> 
                        where TSource: class, new()
                        where TTarget: class, new()
        {
            public static TTarget Copy(TSource src, TTarget trg, params string[] properties)
            {
                if (src==null) return trg;
                if (trg == null) trg = new TTarget();
                var fulllist = src.GetType().GetProperties().Where(c => c.CanWrite && c.CanRead).ToList();
                if (properties != null && properties.Count() > 0)
                    fulllist = fulllist.Where(c => properties.Contains(c.Name)).ToList();
                if (fulllist == null || fulllist.Count() == 0) return trg;

                fulllist.ForEach(c =>
                    {
                        c.SetValue(trg, c.GetValue(src));
                    });

                return trg;
            }
        }

这是你使用它的方式:

 var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave,
                                                            "Creation",
                                                            "Description",
                                                            "IdTicketStatus",
                                                            "IdUserCreated",
                                                            "IdUserInCharge",
                                                            "IdUserRequested",
                                                            "IsUniqueTicketGenerated",
                                                            "LastEdit",
                                                            "Subject",
                                                            "UniqeTicketRequestId",
                                                            "Visibility");

或复制所有内容:

var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave);

如果使用net.core并且对象是可序列化的,则可以使用

var jsonBin = BinaryData.FromObjectAsJson(yourObject);

then

var yourObjectCloned = jsonBin.ToObjectFromJson<YourType>();

BinaryData在dotnet中,因此您不需要第三方库。它还可以处理类上的属性为Object类型的情况(属性中的实际数据仍然需要可序列化)

映射器执行深度复制。为对象的每个成员创建一个新对象并分配其所有值。它递归地处理每个非基元内部成员。

我建议您选择目前发展最快、最活跃的产品之一。我建议使用UltraMapperhttps://github.com/maurosampietro/UltraMapper

Nuget软件包:https://www.nuget.org/packages/UltraMapper/

对@Konrad和@craastad的添加,使用内置的System.Text.Json for.NET>5

https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-5-0

方法:

public static T Clone<T>(T source)
{
    var serialized = JsonSerializer.Serialize(source);
    return JsonSerializer.Deserialize<T>(serialized);
}

扩展方法:

public static class SystemExtension
{
    public static T Clone<T>(this T source)
    {
        var serialized = JsonSerializer.Serialize(source);
        return JsonSerializer.Deserialize<T>(serialized);
    }
}