复制对象属性:反射或序列化 - 哪个更快?

时间:2011-11-18 11:14:20

标签: c# serialization deserialization deep-copy shallow-copy

我有两个相同类型的对象,需要将属性值从一个对象复制到另一个对象。有两种选择:

  1. 使用反射,浏览第一个对象的属性并复制值。

  2. 序列化第一个对象并反序列化副本。

  3. 两者都符合我的要求,问题是我在速度(成本)方面最好用哪些?

    示例

    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);
    }
    

5 个答案:

答案 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)

二进制序列化非常快我为这类问题使用了很多

Deep cloning objects

答案 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));
        }
    }
}