我想做一些事情,比如:

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

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

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

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


当前回答

我想要一个克隆器,用于非常简单的对象,主要是基元和列表。如果您的对象是开箱即用的JSON可串行化的,那么这个方法就可以了。这不需要修改或实现克隆类上的接口,只需要一个JSON序列化程序,如JSON.NET。

public static T Clone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

此外,您可以使用此扩展方法

public static class SystemExtension
{
    public static T Clone<T>(this T source)
    {
        var serialized = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(serialized);
    }
}

其他回答

C#扩展也将支持“not ISerializable”类型。

 public static class AppExtensions
 {                                                                      
       public static T DeepClone<T>(this T a)
       {
           using (var stream = new MemoryStream())
           {
               var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

               serializer.Serialize(stream, a);
               stream.Position = 0;
               return (T)serializer.Deserialize(stream);
           }
       }                                                                    
 }

用法

       var obj2 = obj1.DeepClone()

除了这里的一些精彩答案外,您可以在C#9.0及更高版本中完成以下操作(假设您可以将类转换为记录):

record Record
{
    public int Property1 { get; set; }

    public string Property2 { get; set; }
}

然后,只需使用with运算符将一个对象的值复制到新对象。

var object1 = new Record()
{
    Property1 = 1,
    Property2 = "2"
};

var object2 = object1 with { };
// object2 now has Property1 = 1 & Property2 = "2"

我希望这有助于:)

我也通过反思看到了它的实现。基本上,有一种方法可以遍历对象的成员,并将它们适当地复制到新对象中。当它到达引用类型或集合时,我认为它对自己进行了递归调用。反射是昂贵的,但效果很好。

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

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

另一个JSON.NET答案。此版本适用于不实现ISerializable的类。

public static class Cloner
{
    public static T Clone<T>(T source)
    {
        if (ReferenceEquals(source, null))
            return default(T);

        var settings = new JsonSerializerSettings { ContractResolver = new ContractResolver() };

        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, settings), settings);
    }

    class ContractResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Select(p => base.CreateProperty(p, memberSerialization))
                .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                    .Select(f => base.CreateProperty(f, memberSerialization)))
                .ToList();
            props.ForEach(p => { p.Writable = true; p.Readable = true; });
            return props;
        }
    }
}