net*_*rog 9 c# serialization deep-copy shallow-copy deserialization
我有两个相同类型的对象,需要将属性值从一个对象复制到另一个对象.有两种选择:
使用反射,浏览第一个对象的属性并复制值.
序列化第一个对象并反序列化副本.
两者都符合我的要求,问题是我在速度(成本)方面更好地使用哪些?
例
class Person
{
public int ID { get; set; }
public string Firsthand { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public decimal Weight { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
需要的属性值从复制Person p1到Person p2.
对于这个简单的样本 - 哪种方法更快?
更新
对于序列化,我使用此处建议的ObjectCopier:深度克隆对象
对于反射,我使用此代码:
foreach (PropertyInfo sourcePropertyInfo in copyFromObject.GetType().GetProperties())
{
PropertyInfo destPropertyInfo = copyToObject.GetType().GetProperty(sourcePropertyInfo.Name);
destPropertyInfo.SetValue(
copyToObject,
sourcePropertyInfo.GetValue(copyFromObject, null),
null);
}
Run Code Online (Sandbox Code Playgroud)
k.m*_*k.m 10
这完全取决于您要复制的内容以及您计划使用的串行器类型.对于序列化器而言,其中一些可能实际上使用反射作为构建对象的基础机制.
编辑#1:据我所知,BinaryFormatter你班级使用的确利用反射来完成它的工作.所以问题是,你能为你的类型编写比微软更好(更快?)的自定义反射代码吗?
编辑#2:出于好奇,我进行了简单的测试.BinaryFormattervs执行浅拷贝的反射.我用过的反射代码可以在这里看到:
var newPerson = Activator.CreateInstance<Person>();
var fields = newPerson.GetType().GetFields(BindingFlags.Public
| BindingFlags.Instance);
foreach (var field in fields)
{
var value = field.GetValue(person);
field.SetValue(newPerson, value);
}
Run Code Online (Sandbox Code Playgroud)
与ObjectCopier您正在使用的课程相比,结果是什么?反射似乎比序列化代码快7倍.然而,这适用于Person具有公共字段的类.对于属性而言,差异仍然是显而易见的,但它只快2倍.
我假设差异来自BinaryFormatter需要使用流的事实,这会引入额外的开销.然而,这只是我的假设,可能与事实相去甚远.
我在这里可以找到我使用的测试程序的源代码.欢迎任何人指出它的缺陷和可能的问题:-)
旁注
与所有"我想知道..."基准一样,我建议你带着一点点盐.只有在其性能实际成为问题时才应进行此类优化.
最终,通用序列化器(例如BinaryFormatter,via ObjectCopier)正在使用反射.如何以及他们使用它取决于具体的序列化,但总是有,如果你序列化涉及额外的开销.
由于您只需要浅拷贝,因此像AutoMapper这样的工具是最合适的工具; 再次,它正在使用反射(但我希望它是"正确的方式",即不通过GetValue()/ SetValue()),但它没有序列化成本.
在这种情况下,序列化是过度的; AutoMapper非常合理.如果你想要深度克隆,它会变得更加棘手......序列化可能会开始变得诱人.我仍然可能不会选择BinaryFormatter自己,但我对序列化非常挑剔; p
当然通过一些基本的反射来做同样的GetValue()事情等等,这当然是微不足道的,但那会很慢.这里另一个有趣的选择是你可以使用ExpressionAPI在运行时创建一个对象复制器......但是...... AutoMapper会在这里完成你需要的所有东西,所以这似乎是多余的工作.