DeepClone(使用二进制序列化)与手动设置属性的性能

时间:2012-02-23 05:54:04

标签: c# performance clone

我有一组对象,我正在尝试克隆这个集合,并尝试理解不同方法的性能含义。

集合中的对象有大约20个属性,所有字符串,整数,浮点数(此对象内部没有任何嵌套对象)。这两种方法是:

  1. 创建DeepClone()方法:

    public static class ExtensionMethods
    {
        public static T DeepClone<T>(this T a)
        {
           using (var stream = new MemoryStream())
           {
               var formatter = new BinaryFormatter();
               formatter.Serialize(stream, a);
               stream.Position = 0;
              return (T)formatter.Deserialize(stream);
           }
       }
    

    }

  2. 手动编写“复制”代码,我循环遍历集合并“新建”新对象,然后手动设置所有20个属性。像这样的东西

     public MyObject Copy(MyObject myObj)
    {
     var obj = new MyObject();
     obj.Prop1 = myObj.Prop1;
     obj.Prop2 = myObj.Prop2;
     return obj;
    

    }

  3. 我得到了非常不一致的结果,所以我希望得到人们的反馈:

    1. 一个人应该比另一个快得多吗?我本以为选择二,但我的测试似乎不支持这个,所以我想弄清楚我做错了什么。

    2. 有没有办法更快地完成这项工作?

4 个答案:

答案 0 :(得分:5)

好吧,首先BinaryFormatter路由肯定要慢一些,因为它使用反射来获取/设置属性。最常见的方法是将IClonable接口与复制构造函数结合使用。

class A : ICloneable
{
    private readonly int _member;

    public A(int member)
    {
        _member = member;
    }

    public A(A a)
    {
        _member = a._member;
    }

    public object Clone()
    {
        return new A(this);
    }
}

当然严格来说,你只需要复制构造函数,这应该是最快的方法。如果您的对象很简单,则应尝试使用内置的MemberwiseClone函数。

class A : ICloneable
{
    private readonly int _member;

    public A(int member)
    {
        _member = member;
    }

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

与此同时,我写了some test code来查看MemberwiseClone()是否比使用复制构造函数更快或更慢。你可以找到它here。我发现MemberwiseClone实际上比执行CopyConstructor慢得多,至少在小类上是这样。请注意,使用BinaryFormatter非常慢。

答案 1 :(得分:2)

在我之前的角色中,我们调查了这个问题,因为我们正在缓存对象,并希望在将它们从缓存中移出之前克隆它们。

我们做了一些详细的基准测试,发现属性设置总是至少快一个数量级然后是BinaryFormatter方法,尽管显然需要手动实现而不是更简单的BinaryFormatter方法。对于深度对象图,IIRC的差异变得更加明显。

最后,我们采取了三管齐下的方法来克隆&#34;:

  • 如果类型是不可变的(我们用标记界面表示IImutable,但你可以同样使用某个属性或某些),我们会&#34;克隆&#34;通过返回原始实例。既然我们知道没有人可以改变它,那么继续返回同一个实例是安全的。显然这是最快的类型&#34;克隆&#34;,虽然显然根本不是克隆。
  • 如果类型实现了我们自己的IDeepCloneable<T>接口(这将是您的第二个示例 - 但是通用的)我们会使用它。 [此通用接口将继承自非通用等效项IDeepCloneable]
  • 如果做不到这一点,我们会回到你的第一个例子BinaryFormatter

我提到了&#34; immutable&#34;因为取决于你正在做什么,有时最好的方法是重新设计你需要克隆的类,这样就根本不需要克隆它们。如果它们在创建后基本上是只读的,这很容易;但即使不是,有时构建器/不可变类型方法也很有用(请参阅框架中的UriUriBuilder。前者基本上是不可变的,而后者可用于构建和/或改变前者的实例。

答案 2 :(得分:0)

使用序列化是一个非常聪明的主意。但是,逐个设置属性应该同样快,并且大多数时候(除非对象非常小)必须更快 - 你做的工作少,而不是使用反射。

您是否可以重现使用序列化比手动复制属性更快的情况?

答案 3 :(得分:0)

序列化肯定比属性赋值慢。

二进制序列化是首选,因为代码易于维护。

相反,属性赋值更好的原因可能是在克隆期间,您不希望克隆对象的所有属性,但希望能够顺序序列化它们。简而言之,您可以更好地控制流量。