C# 更快的深度克隆

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/852064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 04:11:45  来源:igfitidea点击:

Faster deep cloning

c#.net

提问by Karol Kolenda

Does anyone want a framework/class which allows me to clone by values .Net objects? I'm only interested with public read/write properties (namely DataContracts), and I don't care if references are resolved correctly (i.e. collecions which contains the same instance of item twice).

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

I tried serialization trick via DataContractSerializer(serialize to XML and back), wrote reflection-based cloning class (sometimes faster/sometimes slower), and was wondering if someone wrote a helper class which can do this via Emit and not reflection. As for now emitting IL is a little to much for my little brain, but I guess this would be the ultimate solution. Unless someone knows an alternative method which is faster than DataContractSerializer.

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

采纳答案by Marc Gravell

If you are talking about an object tree/graph:

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

Writing specific IL to serialize an object is tricky. IMO, your best bet is to look at a full serialization, like how DataContractSerializerwould work - but not necessarily with that engine.

编写特定的 IL 来序列化对象是很棘手的。IMO,您最好的选择是查看完整的序列化,例如如何DataContractSerializer工作 - 但不一定使用该引擎。

For example, protobuf-nethas a Serializer.DeepClone<T>method that might help. It should be faster than DataContractSerializer, at least. At the current time, you need to add some clues for the serializer (even if just [ProtoContract(ImplicitFields=ImplicitFields.AllPublic)]) - however, the current (incomplete) work-in-progress offers POCO support without attributes.

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



If you are talking about individual objects:

如果您在谈论单个对象:

There are fairly simple things you can do here with Expressionin .NET 3.5; build a dynamic Expressionbased on reflection, and call .Compile(). MiscUtilhas this already:

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

DestType clone = PropertyCopy<DestType>.CopyFrom(original);

With .NET 2.0/3.0 (without Expression) you might consider HyperDescriptorfor similar purposes.

对于 .NET 2.0/3.0(没有Expression),您可能会出于类似目的考虑使用HyperDescriptor

回答by Anton Gogolev

回答by Sam Saffron

Dynamic Method based serialization will be fastest. (Generate a dynamic method using light weight codegen and use it for serialization)

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

You can do 1 method per property/field or one method for the whole object. From my benchmarking doing 1 per property does not give you too much of a performance hit.

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

See the following code, to see how I do this in Media Browser: http://code.google.com/p/videobrowser/source/browse/trunk/MediaBrowser/Library/Persistance/Serializer.cs

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

There are also some unit tests there.

那里也有一些单元测试。

There is fast reflection sample on theinstructionlimit that does exactly what you want.

指令限制上有快速反射示例,可以完全满足您的需求。

see:

看:

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

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

回答by Dirk Vollmar

I don't know whether this suits your requirements exactly, but you could also create a deep clone using a BinaryFormatter. See this answerto a related question (by Binoj Antony):

我不知道这是否完全符合您的要求,但您也可以使用BinaryFormatter. 请参阅相关问题的答案(由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);
        }
    }
}

回答by Cygon

I have written three deep clone methods for .NET some time ago:

前段时间我为.NET编写了三个深度克隆方法:

  • One uses the well-known BinaryFormattertechnique (though I tweaked it so that objects do not need to be serializable in order to be cloned). This was by far the slowest.

  • For the second I used pure reflection. It was at least 6 times faster than cloning with the BinaryFormatter. This one could also be used on Silverlight and the .NET Compact Framework.

  • The third one uses Linq Expression Trees (for runtime MSIL generation). It is 60 times faster than the BinaryFormattertechnique but has a setup time of approximately 2 milliseconds for the first time each class is encountered.

  • 一种使用众所周知的BinaryFormatter技术(尽管我对其进行了调整,以便对象不需要可序列化以进行克隆)。这是迄今为止最慢的。

  • 第二,我使用了纯反射。它至少比使用BinaryFormatter. 这个也可以在 Silverlight 和 .NET Compact Framework 上使用。

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

Logarithmic scale illustrating cloning performance

说明克隆性能的对数刻度

The horizontal axis shows the number of objects cloned (though each cloned object includes several nested objects).

水平轴显示克隆对象的数量(尽管每个克隆对象包括多个嵌套对象)。

The BinaryFormatteris labeled "Serialization" in the chart. The data series "Reflection" is a custom one that copies fields via GetField()/SetField().

BinaryFormatter是在图中标记为“序列化”。数据系列“反射”是一个自定义的,它通过GetField()/复制字段SetField()

I published all three cloning methods as Open Source here:

我在此处将所有三种克隆方法作为开源发布:

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

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

回答by Toxantron

The CGbR Code Generatorcan generate an implementation of ICloneablefor you. All you need is the nuget packageand a partial class definition that implements ICloneable. The generator will do the rest for you:

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<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;
    }
}

回答by frakon

There is probably no full working cloning code made by IL Emit on the internet.

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

But IL Emit is of the same speed as code by Expression Trees, because both methods end up with similar compiled lambda copy functions. Expression Trees are approximately 4x faster than Reflection. The best thing is that Expression Trees general cloning function is available on the internet.

但是 IL Emit 与 Expression Trees 的代码具有相同的速度,因为这两种方法最终都具有类似的编译 lambda 复制函数。Expression Trees比 Reflection 快大约4 倍。最好的是,Expression Trees 的通用克隆功能可在 Internet 上获得

One implemetation by expression trees was already mentionedby Cygon. New thoroughly tested implementation can be found in the CodeProject article Fast Deep Copy by Expression Trees (C#).

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

Use the extension method by

使用扩展方法通过

var copy = originalObject.DeepCopyByExpressionTree();

回答by Alen.Toma

Well! you could write your own Clone Method wich you could specify to ignore or include properties by its attributes. My new library in the link down, uses reflection and FieldInfo to clone the object recursively. i have added it to CodeProject so you will have access to its code soon which you could modify it to your needs.

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

Try it out its very fast and clean, you will love it.
https://www.nuget.org/packages/FastDeepCloner/1.0.1
or
PM> Install-Package FastDeepCloner

尝试一下它非常快速和干净,你会爱上它。
https://www.nuget.org/packages/FastDeepCloner/1.0.1

PM> Install-Package FastDeepCloner

回答by force

There are a lot of libraries that do this operation. You can see benchmark results here:

有很多库可以做这个操作。您可以在此处查看基准测试结果:

In short words, if you need peformance, do it manually, it really faster. Also, some libraries allows to peform shallowcloning (by the question, it is good variant for you), which is faster. And do not use BinaryFormatterif you need any performance.

简而言之,如果您需要性能,请手动进行,它确实更快。此外,一些库允许执行克隆(问题是,它对您来说是一个很好的变体),这更快。BinaryFormatter如果您需要任何性能,请不要使用。

Also, @frakon mentions that Expressions trees have same speed as IL Emit, it is slightly incorrect. Expressions Tree is slightly slower, but it can be used in partially trusted app.

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

Manual 13ms

手动 13ms

DeepCloner (IL Emit) 167ms

DeepCloner (IL 发射) 167ms

DeepCloner (Expressions) 267ms

DeepCloner (表达式) 267ms

CloneExtensions (Expressions) 560ms

CloneExtensions(表达式)560ms

NClone 901ms

NClone 901ms

Clone.Behave! 8551ms

克隆。行为!8551ms

GeorgeCloney 1996ms

乔治克鲁尼 1996ms

Nuclex.Cloning n/a (Crashed)

Nuclex.Cloning 不适用(崩溃)

FastDeepCloner 1882ms

FastDeepCloner 1882ms

BinaryFormatter 15000ms

BinaryFormatter 15000ms