更快的深度克隆

Kar*_*nda 21 .net c#

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

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

Cyg*_*gon 21

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

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

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

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

描述克隆性能的对数标度

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

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

  • 我很好奇序列化总线protobuf-net与这三者的比较. (5认同)

Mar*_*ell 14

如果你在谈论一个对象树/图:

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

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


如果你在谈论个别对象:

Expression在.NET 3.5中,您可以使用相当简单的操作; Expression基于反射构建动态,并调用.Compile().MiscUtil已经有了这个:

DestType clone = PropertyCopy<DestType>.CopyFrom(original);
Run Code Online (Sandbox Code Playgroud)

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


fra*_*kon 5

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

但 IL Emit 与表达式树代码的速度相同,因为两种方法最终都会得到类似的已编译 lambda 复制函数。表达式树比反射快大约 4 倍。最棒的是,Expression Trees 的通用克隆功能可以在互联网上使用

Cygon已经提到了表达式树的一种实现。新的经过彻底测试的实现可以在 CodeProject 文章Fast Deep Copy by Expression Trees (C#)中找到 。

使用扩展方法

var copy = originalObject.DeepCopyByExpressionTree();
Run Code Online (Sandbox Code Playgroud)


for*_*rce 5

有很多库可以执行此操作.你可以看到测试结果这里:

简而言之,如果您需要性能,请手动执行,它确实更快.此外,一些库允许执行克隆(通过这个问题,它是适合您的好方法),这更快.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