我有一组对象,我正在尝试克隆这个集合,并尝试理解不同方法的性能含义。
集合中的对象有大约20个属性,所有字符串,整数,浮点数(此对象内部没有任何嵌套对象)。这两种方法是:
创建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);
}
}
}
手动编写“复制”代码,我循环遍历集合并“新建”新对象,然后手动设置所有20个属性。像这样的东西
public MyObject Copy(MyObject myObj)
{
var obj = new MyObject();
obj.Prop1 = myObj.Prop1;
obj.Prop2 = myObj.Prop2;
return obj;
}
我得到了非常不一致的结果,所以我希望得到人们的反馈:
一个人应该比另一个快得多吗?我本以为选择二,但我的测试似乎不支持这个,所以我想弄清楚我做错了什么。
有没有办法更快地完成这项工作?
答案 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;因为取决于你正在做什么,有时最好的方法是重新设计你需要克隆的类,这样就根本不需要克隆它们。如果它们在创建后基本上是只读的,这很容易;但即使不是,有时构建器/不可变类型方法也很有用(请参阅框架中的Uri
与UriBuilder
。前者基本上是不可变的,而后者可用于构建和/或改变前者的实例。
答案 2 :(得分:0)
使用序列化是一个非常聪明的主意。但是,逐个设置属性应该同样快,并且大多数时候(除非对象非常小)必须更快 - 你做的工作少,而不是使用反射。
您是否可以重现使用序列化比手动复制属性更快的情况?
答案 3 :(得分:0)
序列化肯定比属性赋值慢。
二进制序列化是首选,因为代码易于维护。
相反,属性赋值更好的原因可能是在克隆期间,您不希望克隆对象的所有属性,但希望能够顺序序列化它们。简而言之,您可以更好地控制流量。