C# 通用对象更新程序-将数组转换为列表

C# 通用对象更新程序-将数组转换为列表,c#,generics,C#,Generics,我试图创建一个泛型类,它能够更新对象的任何属性 我使它适用于几种情况,例如单值属性int、string、bool等。。如果属性是IEnumerable,它也可以很好地工作,在这种情况下,列表或数组都可以正常工作 但是我遇到了一个场景,如果属性是List,但要指定的值是数组,或者相反。在标准情况下,我可以简单地使用ToList或ToArray,但在一般情况下,我不知道如何使用ToList或ToArray 这是密码 public static class ObjectUpdater { pu

我试图创建一个泛型类,它能够更新对象的任何属性

我使它适用于几种情况,例如单值属性int、string、bool等。。如果属性是IEnumerable,它也可以很好地工作,在这种情况下,列表或数组都可以正常工作

但是我遇到了一个场景,如果属性是List,但要指定的值是数组,或者相反。在标准情况下,我可以简单地使用ToList或ToArray,但在一般情况下,我不知道如何使用ToList或ToArray

这是密码

public static class ObjectUpdater
{
    public static T Patch<T>(T obj, IEnumerable<KeyValuePair<string, object>> delta)
    {
        if (obj == null || delta == null)
        {
            return obj;
        }
        foreach (var deltaItem in delta)
        {
            Patch(obj, deltaItem.Key, deltaItem.Value);
        }
        return obj;
    }

    private static void Patch<T>(T obj, string propertyName, object value)
    {
        var propertyInfo = obj.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
        if (propertyInfo == null || !propertyInfo.CanRead || !propertyInfo.CanWrite)
        {
            throw new ArgumentException($"Property '{propertyName}' doesn't exist or cannot be updated");
        }
        SetPropertyValue(obj, value, propertyInfo);
    }

    private static void SetPropertyValue(object obj, object value, PropertyInfo propertyInfo)
    {
        if (propertyInfo.PropertyType.IsEnum)
        {
            propertyInfo.SetValue(obj, Convert.ToInt32(value));
            return;
        }

        // property parsing is based on the target property's TryParse() method
        // big / small enough float / DateTime values had issues, as the ToString() might lose precision >> they're handled separately
        if (value is float)
        {
            SetPropertyValueAsFloat(obj, (float)value, propertyInfo);
            return;
        }

        if (value is DateTime)
        {
            SetPropertyValueAsDateTime(obj, (DateTime)value, propertyInfo);
            return;
        }

        var systemType = propertyInfo.PropertyType.UnderlyingSystemType;
        var tryParse = systemType.GetMethod("TryParse", new[] {typeof(string), systemType.MakeByRefType()});
        if (tryParse == null)
        {
            propertyInfo.SetValue(obj, value);
            return;
        }
        var parameters = new object[]
                         {
                             value.ToString(),
                             null
                         };
        var canParse = (bool) tryParse.Invoke(null, parameters);
        propertyInfo.SetValue(obj, canParse ? parameters[1] : value);
    }

    private static void SetPropertyValueAsDateTime(object obj, DateTime value, PropertyInfo propertyInfo)
    {
        // code to handle DateTime value
    }
}

// and two test methods
[Fact]
private void Patch_StringListPropertyFromArrayValue()
{
    var sourceObject = new TestClassWithCollectionProperties
                       {
                           StringListProperty = null
                       };
    var expectedResult = new TestClassWithCollectionProperties
                         {
                             StringListProperty = new List<string>
                                                  {
                                                      "abc",
                                                      "def"
                                                  }
                         };
    var delta = new List<KeyValuePair<string, object>>
                {
                    new KeyValuePair<string, object>("StringListProperty",
                        new []
                        {
                            "abc",
                            "def"
                        })
                };

    var result = ObjectUpdater.Patch(sourceObject, delta);

    result.ShouldBeEquivalentTo(expectedResult);
}

[Fact]
private void Patch_StringArrayPropertyFromListValue()
{
    var sourceObject = new TestClassWithCollectionProperties
                       {
                           StringArrayProperty = null
                       };
    var expectedResult = new TestClassWithCollectionProperties
                         {
                             StringArrayProperty = new[]
                                                   {
                                                       "abc",
                                                       "def"
                                                   }
                         };
    var delta = new List<KeyValuePair<string, object>>
                {
                    new KeyValuePair<string, object>("StringArrayProperty",
                        new List<string>
                        {
                            "abc",
                            "def"
                        })
                };

    var result = ObjectUpdater.Patch(sourceObject, delta);

    result.ShouldBeEquivalentTo(expectedResult);
}
但是由于ObjectUpdater.Patch抛出System.ArgumentException并显示以下消息,此测试失败

“System.String[]”类型的对象无法转换为“System.Collections.Generic.List`1[System.String]”类型


有什么建议吗?

好吧,在tryParse尝试之前,您可以在代码中的某个地方处理具体情况,如下所示:

if (value != null && value.GetType() != propertyInfo.PropertyType
    // from T[]
    && value.GetType().IsArray && value.GetType().GetArrayRank() == 1
    // to List<T>
    && propertyInfo.PropertyType.IsGenericType
    && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>)
    && propertyInfo.PropertyType.GetGenericArguments()[0] == value.GetType().GetElementType())
{
    var T = value.GetType().GetElementType();
    var listT = typeof(List<>).MakeGenericType(T);
    // new List<T>(IEnumerable<T> items)
    var enumerableT = typeof(IEnumerable<>).MakeGenericType(T);
    var newListT = listT.GetConstructor(new Type [] { enumerableT });
    var list = newListT.Invoke(new[] { value });
    propertyInfo.SetValue(obj, list);
    return;
}

隐马尔可夫模型。。您可以尝试使用将列表转换为数组。要阵列您的东西,并将其转换回循环中的列表?是的,它确实可以处理数组>>列表的情况。您是否也可以提供列表>>阵列方案的版本?当然可以。您只需要通过反射检测case和call List.ToArray方法。但请注意,您正在进入一个无休止的循环:不管怎样,就是这样。
if (value != null && value.GetType() != propertyInfo.PropertyType
    // to T[]
    && propertyInfo.PropertyType.IsArray && propertyInfo.PropertyType.GetArrayRank() == 1
    // from List<T>
    && value.GetType().IsGenericType
    && value.GetType().GetGenericTypeDefinition() == typeof(List<>)
    && value.GetType().GetGenericArguments()[0] == propertyInfo.PropertyType.GetElementType())
{
    // List<T>.ToArray()
    var toArray = value.GetType().GetMethod("ToArray", Type.EmptyTypes);
    var array = toArray.Invoke(value, null);
    propertyInfo.SetValue(obj, array);
    return;
}