我想做类似这样的事情:
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;
}
我也见过通过反射实现这个功能的方法。基本上有一个方法会迭代对象的成员,并将它们适当地复制到新对象中。当它遇到引用类型或集合类型时,它会对自身进行递归调用。反射是比较耗费资源的,但它的效果还不错。
由于在不同项目中找不到符合我所有要求的克隆器,因此我创建了一个深度克隆器,可以配置和适应不同的代码结构,而不是调整我的代码以满足克隆器的要求。这是通过向将被克隆的代码添加注释来实现的,或者您可以将代码保留为默认行为。它使用反射、类型缓存,并基于fasterflect。与其他基于反射/序列化的算法相比,对于大量数据和高对象层次结构,克隆过程非常快速。
https://github.com/kalisohn/CloneBehave
还可以作为nuget包使用: https://www.nuget.org/packages/Clone.Behave/1.0.0
例如:以下代码将深层复制Address,但仅执行_currentJob字段的浅层复制。
public class Person
{
[DeepClone(DeepCloneBehavior.Shallow)]
private Job _currentJob;
public string Name { get; set; }
public Job CurrentJob
{
get{ return _currentJob; }
set{ _currentJob = value; }
}
public Person Manager { get; set; }
}
public class Address
{
public Person PersonLivingHere { get; set; }
}
Address adr = new Address();
adr.PersonLivingHere = new Person("John");
adr.PersonLivingHere.BestFriend = new Person("James");
adr.PersonLivingHere.CurrentJob = new Job("Programmer");
Address adrClone = adr.Clone();
//RESULT
adr.PersonLivingHere == adrClone.PersonLivingHere //false
adr.PersonLivingHere.Manager == adrClone.PersonLivingHere.Manager //false
adr.PersonLivingHere.CurrentJob == adrClone.PersonLivingHere.CurrentJob //true
adr.PersonLivingHere.CurrentJob.AnyProperty == adrClone.PersonLivingHere.CurrentJob.AnyProperty //true
在IT技术领域,我们见过很多关于序列化、手动实现以及反射的想法,但是我想提出一种全新的方法:使用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 AnyObject(AnyObject anyObject)
{
foreach (var property in typeof(AnyObject).GetProperties())
{
property.SetValue(this, property.GetValue(anyObject));
}
foreach (var field in typeof(AnyObject).GetFields())
{
field.SetValue(this, field.GetValue(anyObject));
}
}
private static MyObj DeepCopy(MyObj source)
{
var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings);
}
Use it like this: MyObj a = DeepCopy(b);
以下是一个快速且易于实现的解决方案,无需依赖Serialization/Deserialization(序列化/反序列化)。
public class MyClass
{
public virtual MyClass DeepClone()
{
var returnObj = (MyClass)MemberwiseClone();
var type = returnObj.GetType();
var fieldInfoArray = type.GetRuntimeFields().ToArray();
foreach (var fieldInfo in fieldInfoArray)
{
object sourceFieldValue = fieldInfo.GetValue(this);
if (!(sourceFieldValue is MyClass))
{
continue;
}
var sourceObj = (MyClass)sourceFieldValue;
var clonedObj = sourceObj.DeepClone();
fieldInfo.SetValue(returnObj, clonedObj);
}
return returnObj;
}
}
编辑:需要
using System.Linq;
using System.Reflection;
这是我如何使用它的方法
public MyClass Clone(MyClass theObjectIneededToClone)
{
MyClass clonedObj = theObjectIneededToClone.DeepClone();
}
using System;
public class Program
{
public class Example
{
public string A { get; set; }
}
public record ClonerRecord<T>(T a)
{
}
public static void Main()
{
var foo = new Example {A = "Hello World"};
var bar = (new ClonerRecord<Example>(foo) with {}).a;
foo.A = "Goodbye World :(";
Console.WriteLine(bar.A);
}
}
using System;
public class Program
{
public record Example
{
public string A { get; set; }
}
public static void Main()
{
var foo = new Example {A = "Hello World"};
var bar = foo with {};
foo.A = "Goodbye World :(";
Console.WriteLine(bar.A);
}
}
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression
record
克隆一个 class
是不起作用的- https://dotnetfiddle.net/w3IJgG;但是对于一个扁平的 record
进行克隆似乎可以按值复制!https://dotnetfiddle.net/MCHGEL - IzzyObjectExtensions.cs
的副本。它已经有9年历史了。虽然它能够工作,但我们后来意识到对于较大的对象结构来说它非常缓慢。C# extension method for fast object cloning.
This is a speed-optimized fork of Alexey Burtsev's deep copier. Depending on your usecase, this will be 2x - 3x faster than the original. It also fixes some bugs which are present in the original code. Compared to the classic binary serialization/deserialization deep clone technique, this version is about seven times faster (the more arrays your objects contain, the bigger the speedup factor).
The speedup is achieved via the following techniques:
- object reflection results are cached
- don't deep copy primitives or immutable structs & classes (e.g. enum and string)
- to improve locality of reference, process the 'fast' dimensions or multidimensional arrays in the inner loops
- use a compiled lamba expression to call MemberwiseClone
How to use:
using Baksteen.Extensions.DeepCopy; ... var myobject = new SomeClass(); ... var myclone = myobject.DeepCopy()!; // creates a new deep copy of the original object
Note: the exclamation mark (null-forgiving operator) is only required if you enabled nullable referency types in your project