更快的深度克隆

有没有人想要一个允许我通过值.Net对象克隆的框架/类? 我只对公共读/写属性(即DataContracts)感兴趣,我不关心引用是否正确解析(即包含相同项目实例两次的集合)。

我尝试通过DataContractSerializer序列化技巧(序列化为XML并返回),编写基于reflection的克隆类(有时更快/有时更慢),并且想知道是否有人写了一个助手类,可以通过Emit而不是reflection来做到这一点。 至于现在发射的IL对我的小脑子来说有点太多了,但我想这将是最终的解决方案。 除非有人知道比DataContractSerializer更快的替代方法。

如果您正在谈论对象树/图:

编写特定的IL以序列化对象是棘手的。 IMO,你最好的选择是看一个完整的序列化,比如DataContractSerializer如何工作 – 但不一定是那个引擎。

例如, protobuf-net有一个可能有用的Serializer.DeepClone方法。 它应该比DataContractSerializer更快,至少。 目前,您需要为序列化程序添加一些线索(即使只是[ProtoContract(ImplicitFields=ImplicitFields.AllPublic)] ) – 但是,当前(不完整)正在进行的工作提供了没有属性的POCO支持。


如果你在谈论个别对象:

你可以在.NET 3.5中使用Expression做一些相当简单的事情; 基于reflection构建动态Expression ,并调用.Compile() 。 MiscUtil已经有了这个:

 DestType clone = PropertyCopy.CopyFrom(original); 

使用.NET 2.0 / 3.0(无Expression ),您可能会将HyperDescriptor视为类似用途。

我不久前为.NET编写了三种深度克隆方法:

  • 一个人使用众所周知的BinaryFormatter技术(虽然我调整它以便对象不需要可序列化以便克隆)。 这是迄今为止最慢的。

  • 对于第二个我使用纯粹的reflection。 它比使用BinaryFormatter克隆快至少6倍。 这个也可用于Silverlight和.NET Compact Framework。

  • 第三个使用Linq表达式树(用于运行时MSIL生成)。 它比BinaryFormatter技术快60倍,但是遇到每个类的第一次设置时间约为2毫秒。

描述克隆性能的对数标度

我在这里发布了所有三种克隆方法作为开源:

http://blog.nuclex-games.com/mono-dotnet/fast-deep-cloning/

有很多库可以执行此操作。 您可以在此处查看基准测试结果

简而言之,如果您需要性能,请手动执行,它确实更快。 此外,一些库允许执行克隆(通过问题,它是适合您的好方法),这更快。 如果您需要任何性能,请不要使用BinaryFormatter

此外,@ frakon提到表达式树的速度与IL Emit相同,略有不正确。 表达式树稍慢,但它可以在部分受信任的应用程序中使用。

手动13ms

DeepCloner(IL Emit)167ms

DeepCloner(表达式)267ms

CloneExtensions(表达式)560ms

NClone 901ms

Clone.Behave! 8551ms

GeorgeCloney 1996ms

Nuclex.Cloning n / a(崩溃)

FastDeepCloner 1882ms

BinaryFormatter 15000ms

尝试AutoMapper或BLToolkit Mapping

我不知道这是否完全符合您的要求,但您也可以使用BinaryFormatter创建深度克隆。 查看相关问题的答案 ( Binoj Antony ):

 public static class GenericCopier { public static T DeepCopy(object objectToCopy) { using (MemoryStream memoryStream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, objectToCopy); memoryStream.Seek(0, SeekOrigin.Begin); return (T) binaryFormatter.Deserialize(memoryStream); } } } 

IL Emit可能没有在互联网上制作完整的克隆代码。

但是IL Emit与Expression Trees的代码速度相同,因为这两种方法最终都具有类似的编译lambda拷贝函数。 表达树比reflection快大约4倍 。 最好的是Expression Trees通用克隆function可以在互联网上找到

Cygon 已经提到过表达树的一种实现方式。 可以在CodeProject文章快速深层复制表达式树(C#)中找到新的经过彻底测试的实现。

使用扩展方法

 var copy = originalObject.DeepCopyByExpressionTree(); 

基于动态方法的序列化将是最快的。 (使用轻量级codegen生成动态方法并将其用于序列化)

每个属性/字段可以执行1个方法,也可以为整个对象执行一个方法。 从我的基准测试中,每个属性执行1次并不会给您带来太多的性能损失。

请参阅以下代码,了解我如何在媒体浏览器中执行此操作: http : //code.google.com/p/videobrowser/source/browse/trunk/MediaBrowser/Library/Persistance/Serializer.cs

那里还有一些unit testing。

在指令限制上有快速reflection样本,可以完全按照您的要求进行操作。

看到:

http://theinstructionlimit.com/?p=76

CGbR代码生成器可以为您生成ICloneable的实现。 您只需要nuget包和实现ICloneable的部分类定义。 发电机将为您完成剩下的工作:

 public partial class Root : ICloneable { public Root(int number) { _number = number; } private int _number; public Partial[] Partials { get; set; } public IList 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(Numbers.Count); for (var i = 0; i < Numbers.Count; i++) { var value = Numbers[i]; tempNumbers[i] = value; } copy.Numbers = tempNumbers; } else { // In a shallow clone only references are copied copy.Partials = Partials; copy.Numbers = Numbers; } return copy; } } 

好! 您可以编写自己的克隆方法,您可以指定忽略或包含属性的属性。 链接中的新库,使用reflection和FieldInfo递归克隆对象。 我已将其添加到CodeProject,因此您可以很快访问其代码,您可以根据需要对其进行修改。

尝试它非常快速和干净,你会喜欢它。
https://www.nuget.org/packages/FastDeepCloner/1.0.1
要么
PM>安装包FastDeepCloner