我想做一些事情,比如:
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();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
当前回答
下面是一个深度拷贝实现:
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;
}
其他回答
简单的答案是从ICloneable接口继承,然后实现.clone函数。克隆应该按成员进行复制,并对任何需要它的成员执行深度复制,然后返回结果对象。这是一个递归操作(它要求您要克隆的类的所有成员都是值类型或实现ICloneable,并且它们的成员是值类型或者实现IClonea,依此类推)。
有关使用ICloneable克隆的详细说明,请参阅本文。
长篇大论的答案是“视情况而定”。正如其他人所提到的,ICloneable不受泛型支持,需要对循环类引用进行特殊考虑,并且实际上被一些人视为.NET Framework中的“错误”。序列化方法取决于对象是否可序列化,而这些对象可能不可序列化,并且您可能无法控制。对于哪种做法是“最佳”做法,社会上仍有很多争论。事实上,没有一个解决方案是适用于所有情况的一刀切的最佳实践,就像ICloneable最初被解释的那样。
请参阅本开发者角文章,了解更多选项(归功于Ian)。
保持简单,并使用AutoMapper,正如其他人提到的,这是一个简单的小库,可以将一个对象映射到另一个对象。。。要将一个对象复制到另一个具有相同类型的对象,只需要三行代码:
MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);
目标对象现在是源对象的副本。不够简单?创建扩展方法以在解决方案中的任何地方使用:
public static T Copy<T>(this T source)
{
T copy = default(T);
Mapper.CreateMap<T, T>();
copy = Mapper.Map<T, T>(source);
return copy;
}
扩展方法可以如下使用:
MyType copy = source.Copy();
我更喜欢复制构造函数而不是克隆。意图更加明确。
复制所有公共财产的简单扩展方法。适用于任何对象,不要求类为[Serializable]。可以扩展到其他访问级别。
public static void CopyTo( this object S, object T )
{
foreach( var pS in S.GetType().GetProperties() )
{
foreach( var pT in T.GetType().GetProperties() )
{
if( pT.Name != pS.Name ) continue;
( pT.GetSetMethod() ).Invoke( T, new object[]
{ pS.GetGetMethod().Invoke( S, null ) } );
}
};
}
下面是一个深度拷贝实现:
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;
}