我有两个相同类型的对象,需要将属性值从一个对象复制到另一个对象。有两种选择:
使用反射,浏览第一个对象的属性并复制值。
序列化第一个对象并反序列化副本。
两者都符合我的要求,问题是我在速度(成本)方面最好用哪些?
示例
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; }
}
需要将属性值从Person p1
复制到Person p2
。
对于这个简单的样本 - 哪种方法更快?
更新
对于序列化,我使用此处建议的ObjectCopier:Deep cloning objects
对于反射,我使用此代码:
foreach (PropertyInfo sourcePropertyInfo in copyFromObject.GetType().GetProperties())
{
PropertyInfo destPropertyInfo = copyToObject.GetType().GetProperty(sourcePropertyInfo.Name);
destPropertyInfo.SetValue(
copyToObject,
sourcePropertyInfo.GetValue(copyFromObject, null),
null);
}
答案 0 :(得分:10)
这完全取决于您要复制的内容以及您计划使用的串行器类型。对于序列化器来说,它们中的某些实际上可能正在使用反射作为构建对象的基础机制。
编辑#1:据我所知,您班级使用的BinaryFormatter
确实利用反射来完成其工作。所以问题是,你能为你的类型编写比微软更好(更快吗?)的自定义反射代码吗?
编辑#2:出于好奇,我进行了简单的测试。执行浅拷贝方面的BinaryFormatter
vs反射。我在这里可以看到反思代码:
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);
}
与您使用的ObjectCopier
课程相比,有哪些结果?反射似乎比序列化代码执行快7倍。但是,这适用于带有公开字段的Person
类。对于属性而言,差异仍然是显而易见的,但它只快2倍。
我认为差异来自BinaryFormatter
需要使用流的事实,这会引入额外的开销。然而,这只是我的假设,可能远非事实。
我使用的测试程序的源代码可以找到here。欢迎任何人指出它的缺陷和可能的问题: - )
旁注
正如所有“我想知道......”基准一样,我建议你带着一点点盐。只有在其性能实际成为问题时才应进行此类优化。
答案 1 :(得分:8)
最终,通用序列化程序(例如BinaryFormatter
,通过ObjectCopier
)正在使用反射。 well 如何使用它取决于特定的序列化程序,但是如果你进行序列化则总是会产生额外的开销。
由于您只需要浅拷贝,因此像AutoMapper这样的工具是最合适的工具;再次,它使用反射(但我希望它是“正确的方式”,即不通过GetValue()
/ SetValue()
),但它没有序列化成本。
在这种情况下,序列化是过度的; AutoMapper非常合理。如果你想要深度克隆,它会变得更加棘手......序列化可能会开始变得诱人。我仍然可能不会自己选择BinaryFormatter
,但我对序列化非常挑剔; p
通过GetValue()
等对一些基本反射做同样的事情当然是微不足道的,但这样做会很慢。这里另一个有趣的选择是你可以使用Expression
API在运行时创建一个对象复制器....但是...... AutoMapper会在这里完成你需要的所有东西,所以它似乎是多余的工作。
答案 2 :(得分:0)
二进制序列化非常快我为这类问题使用了很多
答案 3 :(得分:0)
如果您在运行时复制属性,则反射就是答案。如果不是在运行时,我会去序列化。 Serialization vs Reflection看一次。
答案 4 :(得分:0)
void Copy(object copyToObject, object copyFromObject)
{
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
FieldInfo[] fields = copyFromObject.GetType().GetFields(flags);
for (int i = 0; i < fields.Length; ++i)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Static;
FieldInfo field = copyFromObject.GetType().GetField(fields[i].Name, bindFlags);
FieldInfo toField = copyToObject.GetType().GetField(fields[i].Name, bindFlags);
if(field != null)
{
toField.SetValue(copyToObject, field.GetValue(copyFromObject));
}
}
}