我想做一些事情,比如:

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

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

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

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


当前回答

使用System.Text.Json:

https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/

public static T DeepCopy<T>(this T source)
{
    return source == null ? default : JsonSerializer.Parse<T>(JsonSerializer.ToString(source));
}

新的API使用Span<T>。这应该很快,最好做一些基准测试。

注意:不需要像Json.NET那样使用ObjectCreationHandling。Replace,因为默认情况下它将替换集合值。您现在应该忘记Json.NET,因为所有的东西都将被新的官方API所取代。

我不确定这是否适用于私人领域。

其他回答

好的,这篇文章中有一些明显的反射示例,但反射通常很慢,直到你开始正确缓存它。

如果你能正确缓存它,那么它将以4.6s的速度深度克隆1000000个对象(由Watcher测量)。

static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();

而不是获取缓存的财产或将新属性添加到字典并简单使用它们

foreach (var prop in propList)
{
        var value = prop.GetValue(source, null);   
        prop.SetValue(copyInstance, value, null);
}

在另一个答案中查看我的帖子中的完整代码

https://stackoverflow.com/a/34365709/4711853

要克隆类对象,可以使用object.MemberswiseColone方法,

只需将此函数添加到类中:

public class yourClass
{
    // ...
    // ...

    public yourClass DeepCopy()
    {
        yourClass othercopy = (yourClass)this.MemberwiseClone();
        return othercopy;
    }
}

然后要执行深度独立复制,只需调用DeepCopy方法:

yourClass newLine = oldLine.DeepCopy();

希望这有帮助。

代码生成器

我们已经看到了很多想法,从序列化到手动实现再到反射,我想使用CGbR代码生成器提出一种完全不同的方法。生成克隆方法具有内存和CPU效率,因此比标准DataContractSerializer快300倍。

你只需要一个带有ICloneable的部分类定义,生成器就可以完成剩下的工作:

public partial class Root : ICloneable
{
    public Root(int number)
    {
        _number = number;
    }
    private int _number;

    public Partial[] Partials { get; set; }

    public IList<ulong> Numbers { get; set; }

    public object Clone()
    {
        return Clone(true);
    }

    private Root()
    {
    }
} 

public partial class Root
{
    public Root Clone(bool deep)
    {
        var copy = new Root();
        // All value types can be simply copied
        copy._number = _number; 
        if (deep)
        {
            // In a deep clone the references are cloned 
            var tempPartials = new Partial[Partials.Length];
            for (var i = 0; i < Partials.Length; i++)
            {
                var value = Partials[i];
                value = value.Clone(true);
                tempPartials[i] = value;
            }
            copy.Partials = tempPartials;
            var tempNumbers = new List<ulong>(Numbers.Count);
            for (var i = 0; i < Numbers.Count; i++)
            {
                var value = Numbers[i];
                tempNumbers.Add(value);
            }
            copy.Numbers = tempNumbers;
        }
        else
        {
            // In a shallow clone only references are copied
            copy.Partials = Partials; 
            copy.Numbers = Numbers; 
        }
        return copy;
    }
}

注意:最新版本有更多的空检查,但为了更好地理解,我省略了它们。

对于克隆过程,可以先将对象转换为字节数组,然后再转换回对象。

public static class Extentions
{
    public static T Clone<T>(this T obj)
    {
        byte[] buffer = BinarySerialize(obj);
        return (T)BinaryDeserialize(buffer);
    }

    public static byte[] BinarySerialize(object obj)
    {
        using (var stream = new MemoryStream())
        {
            var formatter = new BinaryFormatter(); 
            formatter.Serialize(stream, obj); 
            return stream.ToArray();
        }
    }

    public static object BinaryDeserialize(byte[] buffer)
    {
        using (var stream = new MemoryStream(buffer))
        {
           var formatter = new BinaryFormatter(); 
           return formatter.Deserialize(stream);
        }
    }
}

必须为序列化进程序列化对象。

[Serializable]
public class MyObject
{
    public string Name  { get; set; }
}

用法:

MyObject myObj  = GetMyObj();
MyObject newObj = myObj.Clone();

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

 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);