有没有人想要一个允许我按值.Net对象克隆的框架/类?我只对公共读/写属性(即DataContracts)感兴趣,我不关心引用是否正确解析(即包含相同项目实例的集合两次)。
我尝试通过DataContractSerializer
进行序列化技巧(序列化为XML并返回),编写基于反射的克隆类(有时更快/有时更慢),并且想知道是否有人编写了一个可以通过Emit执行此操作的辅助类而不是反思。至于现在发射的IL对我的小脑子来说有点大,但我想这将是最终的解决方案。除非有人知道比DataContractSerializer更快的替代方法。
答案 0 :(得分:20)
我前段时间为.NET编写了三种深度克隆方法:
一个人使用众所周知的BinaryFormatter
技术(虽然我调整了它,因此对象不需要可序列化以便克隆)。这是迄今为止最慢的。
第二次,我使用纯粹的反射。它比使用BinaryFormatter
克隆快至少6倍。这个也可用于Silverlight和.NET Compact Framework。
第三个使用Linq表达式树(用于运行时MSIL生成)。它比BinaryFormatter
技术快60倍,但遇到每个类的第一次设置时间约为2毫秒。
我在这里发布了所有三种克隆方法作为开源:
答案 1 :(得分:14)
如果您正在谈论对象树/图表:
编写特定的IL以序列化对象是棘手的。 IMO,您最好的选择是查看完整的序列化,例如DataContractSerializer
的工作方式 - 但不一定是该引擎。
例如,protobuf-net有Serializer.DeepClone<T>
方法可能有所帮助。它应该比DataContractSerializer
快,至少。目前,您需要为序列化程序添加一些线索(即使只是[ProtoContract(ImplicitFields=ImplicitFields.AllPublic)]
) - 但是,当前(不完整)正在进行的工作提供了没有属性的POCO支持。
如果你在谈论个别对象:
在.NET 3.5中使用Expression
可以执行相当简单的操作;根据反射构建动态Expression
,并调用.Compile()
。 MiscUtil已经有了这个:
DestType clone = PropertyCopy<DestType>.CopyFrom(original);
使用.NET 2.0 / 3.0(不含Expression
)时,您可能会考虑HyperDescriptor用于类似目的。
答案 2 :(得分:5)
有很多库可以执行此操作。您可以看到基准测试结果here:
简而言之,如果您需要性能,请手动执行,它确实更快。此外,一些库允许执行浅克隆(通过问题,它是适合您的好方法),这更快。如果您需要任何表现,请不要使用BinaryFormatter
。
另外,@ frakon提到表达式树的速度与IL Emit相同,略有不正确。表达式树稍慢,但可以在部分受信任的应用程序中使用。
手动13毫秒
DeepCloner(IL Emit)167ms
DeepCloner(表达式)267ms
CloneExtensions(表达式)560毫秒
NClone 901ms
Clone.Behave! 8551ms
GeorgeCloney 1996ms
Nuclex.Cloning n / a(Crashed)
FastDeepCloner 1882ms
BinaryFormatter 15000ms
答案 3 :(得分:4)
IL Emit可能没有在互联网上制作完整的克隆代码。
但是IL Emit与Expression Trees的代码速度相同,因为这两种方法都以类似的编译lambda拷贝函数结束。表达式树比反射快<4倍。最好的事情是 Expression Trees一般克隆功能可在互联网上找到。
Cygon表达式树was already mentioned的一个实现。可以在CodeProject文章中找到经过全面测试的新实现 的 Fast Deep Copy by Expression Trees (C#) 强>
使用
的扩展方法var copy = originalObject.DeepCopyByExpressionTree();
答案 4 :(得分:3)
答案 5 :(得分:3)
我不知道这是否完全符合您的要求,但您也可以使用BinaryFormatter
创建深度克隆。请参阅this answer相关问题(Binoj Antony):
public static class GenericCopier<T>
{
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);
}
}
}
答案 6 :(得分:1)
基于动态方法的序列化将是最快的。 (使用轻量级codegen生成动态方法并将其用于序列化)
每个属性/字段可以执行1个方法,也可以为整个对象执行一个方法。从我的基准测试中,每个属性执行1次并不会给您带来太多的性能损失。
请参阅以下代码,了解我如何在媒体浏览器中执行此操作:http://code.google.com/p/videobrowser/source/browse/trunk/MediaBrowser/Library/Persistance/Serializer.cs
那里还有一些单元测试。
在指令极限上有快速反射样本,可以完全按照您的要求进行操作。
见:
答案 7 :(得分:0)
CGbR Code Generator可以为您生成ICloneable
的实施。您所需要的只是nuget package和实现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[i] = value;
}
copy.Numbers = tempNumbers;
}
else
{
// In a shallow clone only references are copied
copy.Partials = Partials;
copy.Numbers = Numbers;
}
return copy;
}
}
答案 8 :(得分:-1)
嘛!您可以编写自己的克隆方法,您可以指定忽略或包含属性的属性。 链接中的新库,使用反射和FieldInfo递归克隆对象。 我已将其添加到CodeProject,因此您可以很快访问其代码,您可以根据需要对其进行修改。
快速干净地尝试它,你会喜欢它。
https://www.nuget.org/packages/FastDeepCloner/1.0.1
要么
PM&GT;安装包FastDeepCloner