仅从一个对象中复制仅填充的值,这些对象尚未填充在同一类型的另一个对象中

时间:2013-09-17 10:36:06

标签: c# reflection

我要求从一个对象中复制仅填充的值,这些对象尚未填充在同一类型的另一个对象中。

例如,我们传递了一个对象,它只是部分地用数据实例化,我们读取数据库以获得对象的完全实例化版本 - 但是这可能没有由提交到数据库的应用程序进行更改 - 因此我们需要将数据库版本中的任何值移动到对象的传入版本中 - 而不会覆盖传入对象中可能已存在的任何值(因为这些是最新的值)。

Adam Robinson在另一篇文章中提出的以下代码(见下文非常有用 - 谢谢!)是一个很好的起点。但是我需要扩展它 - 因为我只想复制目标对象上尚未填充的值(即需要检查destProperty不为null)。然而,作为一个额外的复杂性,在传入的对象中声明了内部复杂类型,此代码复制高级子组而不进入子组的各个属性(即使用Root cdt声明的任何变量我可以尝试和检查是否为null,但是只需复制子cdts中的所有字段而不通过各个字段。)

非常感谢任何帮助。

public static void CopyPropertyValues(object source, object destination)
{
    var destProperties = destination.GetType().GetProperties();

foreach (var sourceProperty in source.GetType().GetProperties())
{
    foreach (var destProperty in destProperties)
    {
        if (destProperty.Name == sourceProperty.Name && 
    destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
        {
            destProperty.SetValue(destination, sourceProperty.GetValue(
                source, new object[] { }), new object[] { });

            break;
        }
    }
}

}

1 个答案:

答案 0 :(得分:0)

首先,您需要确定书中填充的含义。 确定后,请编写以下IsDefaultValue方法的自己版本。 我写的内容将以true以下列方式回答:

  • 如果是bool,则需要false
  • 如果是int则需要0
  • 如果是class则需要null

所以这是我的方法版本:

public static bool IsDefaultValue(object @object) {
    if (null == @object)
        return true;
    else if (@object.GetType().IsValueType) {
        var isDefault = Activator.CreateInstance(@object.GetType()).Equals(@object);
        return isDefault;
    } else
        return false;
}

然后,假设您只对非索引器属性感兴趣,那么您没有层次结构,并且您将始终使用相同类型的对象调用此方法, 您可以过滤掉source中非默认属性但destination中默认属性。

然后,当您在目标属性中具有非默认值时,可以递归地遍历实例图。

请注意,我在这里所写的内容只是演示了如何完成任务。在您的特定场景中有一些错综复杂的细节,您需要自己解决,因为从您的问题来看,这些细节并不明显。

例如,我认为为递归遍历添加停止条件(remainingDepth参数)

是个好主意
public static void CopyPropertyValues(object source, object destination, int remainingDepth = 3) {
    // we've reached the farthest point we're allowed to go to
    // anything beyond this point won't be affected by this method
    if (remainingDepth == 0)
        return;
    // just a check to make sure the following lines won't backfire
    if ((null == source) || (null == destination))
        throw new ArgumentNullException();

    // we'll need to also check that the 2 objects are of the same type
    var type = source.GetType();
    if (destination.GetType() != type)
        throw new ArgumentException("The two objects should be of the same type");

    var properties = type.GetProperties()
        // just filter out the properties which are indexers (if any)
        // and also those properties which are read or write only
        .Where(property => (property.GetIndexParameters().Length == 0) &&
                           property.CanRead && property.CanWrite);

    foreach (var property in properties) {
        var sourceValue = property.GetValue(source, null);
        var destValue = property.GetValue(destination, null);

        if (!IsDefaultValue(sourceValue))
            if (IsDefaultValue(destValue))
                property.SetValue(destination, sourceValue, null);
            else
                if (sourceValue.GetType() == destValue.GetType())
                    CopyPropertyValues(sourceValue, destValue, remainingDepth - 1);   
    }

}

请注意,只需要一次属性枚举,因为对象(正如您在评论部分中所说的那样)属于同一类型。

当表现很重要时,请注意反思。