11

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

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

5 回答 5

13

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

编辑#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需要使用流的事实,这会引入额外的开销。然而,这只是我的假设,可能与事实相去甚远。

我使用的测试程序的源代码可以在这里找到。欢迎任何人指出缺陷和可能的问题:-)


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

于 2011-11-18T11:32:13.743 回答
8

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

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

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

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

于 2011-11-18T11:42:51.403 回答
0

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

深度克隆对象

于 2011-11-18T11:19:22.613 回答
0

如果您在运行时复制属性,反射将是答案。如果不在运行时,我会进行序列化。 序列化与反射看看这个。

于 2011-11-18T11:20:57.823 回答
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));
        }
    }
}
于 2017-08-10T05:41:48.983 回答