更快的克隆方法

6

我正在尝试优化一个复制对象的代码:

#region ICloneable
public object Clone()
{
    MemoryStream buffer = new MemoryStream();
    BinaryFormatter formatter = new BinaryFormatter();

    formatter.Serialize(buffer, this);     // takes 3.2 seconds
    buffer.Position = 0;
    return formatter.Deserialize(buffer);  // takes 2.1 seconds
}
#endregion

这是相当标准的事情。问题在于对象非常庞大,需要5.4秒(根据ANTS Profiler - 我确定有分析器开销,但仍然很长)。

是否有更好、更快的克隆方法?


这完全取决于你想要克隆什么。 - Will Vousden
请参见https://dev59.com/KHRA5IYBdhLWcg3wuAYo。 - nawfal
5个回答

9
  1. 不要实现ICloneable接口。

  2. 快速克隆对象的方法是创建一个与原始实例相同类型的新实例,并将所有字段从原始实例复制/克隆到新实例中。不要试图想出一个“通用”的克隆方法,可以克隆任何类的任何对象。

示例:

class Person
{
    private string firstname;
    private string lastname;
    private int age;

    public Person(string firstname, string lastname, int age)
    {
        this.firstname = firstname;
        this.lastname = lastname;
        this.age = age;
    }

    public Person Clone()
    {
        return new Person(this.firstname, this.lastname, this.age);
    }
}

3
我同意。我还想补充一个选项(3),使用不可变类型,这样你就不需要必须克隆。 - Aaronaught
使用CGbR可以让你获得相同的结果,而无需自己编写代码。 - Toxantron

4

答案: 有更好的克隆方法。

反射 或者 表达式树序列化 更快 (反射快5倍,表达式树快20倍)。

enter image description here

如果您使用作为扩展方法的此链接的克隆函数,您的每个克隆代码都会缩短:

#region ICloneable
public object Clone()
{
    return this.DeepCopyByExpressionTree();
}
#endregion

要使用扩展方法,只需要在您的解决方案中任意位置拥有文件 DeepCopyByExptressionTrees.cs 即可。


1
因为手动复制字段是最快的方式,所以我创建了一个代码生成器,它会读取您的类定义并生成一个克隆方法。您只需要使用CGbR 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;
    }
}

而且这是一个部分类

public partial class Partial : ICloneable
{
    public short Id { get; set; }

    public string Name { get; set; }

    public object Clone()
    {
        return Clone(true);
    }
}

public partial class Partial
{
    public Partial Clone(bool deep)
    {
        var copy = new Partial();
        // All value types can be simply copied
        copy.Id = Id; 
        copy.Name = Name; 
        return copy;
    }
}

1

这是一种相当昂贵的克隆方式。对象从未进入网络,因此所有进行序列化的时间基本上都是浪费的。逐成员克隆将更快。我知道这不是自动化解决方案,但它将是最快的。

大致如此:

class SuperDuperClassWithLotsAndLotsOfProperties {
  object Clone() {
    return new SuperDuperClassWithLotsAndLotsOfProperties {
      Property1 = Property1,
      Property2 = Property2,
    }

  public string Property1 {get;set;}
  public string Property2 {get;set;}
  }
}

1

据我所知,流,即使是像这样的内部流,也是很昂贵的。
你尝试过只是创建一个新对象并更新相关字段以将对象带到相同的状态吗?我很难相信你的方法需要更少的时间。


相对而言,用于序列化和反序列化的反射机制是比较耗费资源的。 - Guffa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接