我試圖做一個泛型類,它能夠更新對象的任何屬性。通用對象更新器 - 將數組轉換爲列表
我使它適用於多種情況,例如,單值屬性(int
,string
,bool
等)。如果屬性爲IEnumerable<T>
,那麼它也可以很好地工作,在這種情況下,列表或數組可以正常工作。
但是如果屬性爲List<T>
,那麼我會碰壁,但要分配的值是Array
(或其他方式)。在「標準」情況下,我可以簡單地使用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 []」不能被轉換爲類型失敗「系統.Collections.Generic.List`1 [System.String]」。
有什麼建議嗎?
嗯..你可以試試你的清單,'.ToArray轉換成數組()'做你的東西,並將其轉換回循環內的'List'也許? – uTeisT