0

我需要仅从一个对象中复制尚未填充到另一个相同类型对象中的填充值。

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

Adam Robinson 在另一篇文章中建议的以下代码(见下文非常有用 - 谢谢!)是一个很好的起点。但是我需要扩展它——因为我只想复制目标对象上尚未填充的值(即需要检查 destProperty 是否为空)。然而,作为一个额外的复杂因素,在传入的对象中声明了内部复杂类型,此代码复制高级子组而不进入子组的各个属性(即我可以尝试使用 Root cdt 声明的任何变量)检查是否为空,但子 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;
        }
    }
}

}

4

1 回答 1

0

首先,您需要确定书中填充的含义。一旦确定了这一点,请编写您自己的以下IsDefaultValue方法版本。我所写的将true通过以下方式回答:

  • 如果是 abool那么它需要具有false价值
  • 如果是,int那么它必须是0
  • 如果有的话,class那么需要null
  • ETC

所以这是我的方法版本:

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

}

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

当性能很重要时,请注意反射。

于 2013-09-17T10:52:44.397 回答