我想做一些事情,比如:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
我想做一些事情,比如:
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);
}
}
其他回答
下面是一个深度拷贝实现:
public static object CloneObject(object opSource)
{
//grab the type and create a new instance of that type
Type opSourceType = opSource.GetType();
object opTarget = CreateInstanceOfType(opSourceType);
//grab the properties
PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//iterate over the properties and if it has a 'set' method assign it from the source TO the target
foreach (PropertyInfo item in opPropertyInfo)
{
if (item.CanWrite)
{
//value types can simply be 'set'
if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
{
item.SetValue(opTarget, item.GetValue(opSource, null), null);
}
//object/complex types need to recursively call this method until the end of the tree is reached
else
{
object opPropertyValue = item.GetValue(opSource, null);
if (opPropertyValue == null)
{
item.SetValue(opTarget, null, null);
}
else
{
item.SetValue(opTarget, CloneObject(opPropertyValue), null);
}
}
}
}
//return the new item
return opTarget;
}
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()
最短的方式,但需要依赖:
using Newtonsoft.Json;
public static T Clone<T>(T source) =>
JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
对于克隆过程,可以先将对象转换为字节数组,然后再转换回对象。
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();
当使用Marc Gravells protobuf net作为序列化程序时,接受的答案需要一些轻微的修改,因为要复制的对象不会被归因于[Serializable],因此不可序列化,Clone方法将抛出异常。我修改了它以与protobuf net一起使用:
public static T Clone<T>(this T source)
{
if(Attribute.GetCustomAttribute(typeof(T), typeof(ProtoBuf.ProtoContractAttribute))
== null)
{
throw new ArgumentException("Type has no ProtoContract!", "source");
}
if(Object.ReferenceEquals(source, null))
{
return default(T);
}
IFormatter formatter = ProtoBuf.Serializer.CreateFormatter<T>();
using (Stream stream = new MemoryStream())
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
这将检查[ProtoControl]属性的存在,并使用protobufs自己的格式化程序来序列化对象。