【问题标题】:Copy object properties: reflection or serialization - which is faster?复制对象属性:反射或序列化——哪个更快?
【发布时间】:2011-11-18 11:14:20
【问题描述】:

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

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

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

两者都符合我的要求,问题是在速度(成本)方面我应该更好地使用哪个?

示例

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

【问题讨论】:

  • autoMapper automapper.org 怎么样?还是只用 Clone() 完成这项工作?
  • 为什么不能正常分配?
  • @Miau,这似乎是一个复杂的属性复制系统,感谢链接,非常有趣。但就我目前的需要而言,我要求它很简单,所以我认为 AutoMapper 将成为这种情况的典范。但我肯定会检查它并将其用于复杂的任务。
  • @DavidKemp,因为它必须是适用于任何提供的类的通用系统。
  • @net_prog 那么你想要深拷贝还是浅拷贝?

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


【解决方案1】:

这完全取决于您要复制什么,以及您打算使用哪种序列化程序。序列化器的问题是,一些实际上可能使用反射作为构建对象的底层机制。

编辑#1: 据我所知,您的班级使用的BinaryFormatter 确实利用反射来完成其工作。所以问题是,你能为你的类型编写比微软为一般场景更好(更快?)的自定义反射代码吗?

编辑#2:出于好奇,我运行了简单的测试。 BinaryFormatter 在执行浅拷贝方面与反射相比。我使用的反射代码可以在这里看到:

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找到。欢迎任何人指出缺陷和可能的问题:-)


旁注
与所有“我在想...” 基准测试一样,我建议您对它持保留态度。只有在性能确实成为问题时才应进行此类优化。

【讨论】:

  • 我有基本类型的简单类,例如 int、string 等...我使用此处建议的序列化程序:stackoverflow.com/questions/78536/cloning-objects-in-c-sharp 类的类型由外部代码提供。
  • @net_prog:检查我的编辑。我已经使用这两种方法为您的班级运行了简单的测试。
  • 谢谢,这就是我要找的。我实际上希望该类具有属性,而不是字段,我已经更正了示例。但即使在这种情况下,反射也更快,我可以看到。
【解决方案2】:

最终,通用序列化程序(例如BinaryFormatter,通过ObjectCopier使用反射。他们如何使用它取决于特定的序列化程序,但如果你正在序列化,总是会涉及额外的开销。

既然你只想要一个浅拷贝,像AutoMapper这样的工具是这里最合适的工具;再次,它使用反射(但我希望它以“正确的方式”进行,即不是通过GetValue()/SetValue()),但它没有序列化成本。

在这种情况下,序列化是多余的; AutoMapper 是完全合理的。如果您想要深度克隆,它会变得更加棘手......序列化可能会开始变得很诱人。我自己可能还是不会选择BinaryFormatter,但是我对序列化很挑剔;p

通过GetValue() 等来纠正一些基本反射当然是微不足道的,但这会很慢。另一个有趣的选择是,您可以使用Expression API 在运行时创建对象复制器......但是...... AutoMapper 在这里完成了您需要的一切,所以这似乎是多余的工作。

【讨论】:

    【解决方案3】:

    二进制序列化速度非常快我经常用它来解决这类问题

    Deep cloning objects

    【讨论】:

    • 对于序列化,我使用该线程中的 ObjectCopier,它比反射快吗?
    • @net_prog 你认为BinaryFormatterObjectCopier 下的序列化器)如何获取它的数据?提示:它以“r”开头,听起来像“deflection”。
    【解决方案4】:

    如果您在运行时复制属性,那么反射就是答案。如果不在运行时,我会进行序列化。 Serialization vs Reflection 看看这个。

    【讨论】:

    • 如果你在运行时知道对象的属性,反射会浪费时间,序列化就是答案。但是如果你在运行时不知道对象的属性,反射是我能想到的唯一解决方案。
    • 如果你知道属性,为什么序列化突然比反射更好?不确定这是否有意义......
    • 因为在运行时不知道对象类型时会使用反射。既然已经知道对象的类型,又何必在序列化(二进制序列化)又快又容易实现的时候使用反射呢。
    • 这完全没有意义。序列化将在这里涉及反射;添加实际的编码/解码步骤只会增加工作量。对于反射复制器与序列化的两种合理实现,以及对于浅拷贝,反射复制器在各个方面都更可取。作为一个对反射和序列化都非常了解的人说话。
    【解决方案5】:
    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));
            }
        }
    }
    

    【讨论】:

    • 代码可能会回答这个问题。但是,如果您解释代码的作用以及它如何解决问题,您会为 OP 提供更多帮助。
    猜你喜欢
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-10
    • 2016-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多