C# 反射 - 通过 属性 的路径设置值
C# Reflection - SetValue by path to property
我想通过指定以点分隔的路径来更新任何 public 属性 中的值。
但是每当我调用我的方法时,我都会收到一个错误:
pi.SetValue(instance, value1, null);
错误信息:
对象与目标类型不匹配。
我的方法:
private void SetPathValue(object instance, string path, object value)
{
string[] pp = path.Split('.');
Type t = instance.GetType();
for (int i = 0; i < pp.Length; i++)
{
PropertyInfo pi = t.GetProperty(pp[i]);
if (pi == null)
{
throw new ArgumentException("Properties path is not correct");
}
else
{
instance = pi.GetValue(instance, null);
t = pi.PropertyType;
if (i == pp.Length - 1)//last
{
// Type targetType = IsNullableType(pi.PropertyType) ? Nullable.GetUnderlyingType(pi.PropertyType) : pi.PropertyType;
var value1 = Convert.ChangeType(value, instance.GetType());
pi.SetValue(instance, value1, null);//ERROR
}
}
}
}
private static bool IsNullableType(Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
我认为您的原始版本最终会设置值 "one level too deep"。
我认为递归模式更容易遵循,并且需要更少的代码。这是我拼凑的一个快速版本,适用于简单的测试用例。
有几个优化机会(在递归调用中重建字符串)和边缘情况(如 null
检查)我现在没有时间处理,但我没有不认为他们会太难添加。
public void SetProperty(object target, string property, object setTo)
{
var parts = property.Split('.');
var prop = target.GetType().GetProperty(parts[0]);
if (parts.Length == 1)
{
// last property
prop.SetValue(target, setTo, null);
}
else
{
// Not at the end, go recursive
var value = prop.GetValue(target);
SetProperty(value, string.Join(".", parts.Skip(1)), setTo);
}
}
Here 是一个 LINQPad 演示,展示了它的实际应用:
void Main()
{
var value = new A();
Debug.WriteLine("Original value:");
value.Dump();
Debug.WriteLine("Changed value:");
SetProperty(value, "B.C.D","changed!");
value.Dump();
}
public void SetProperty(object target, string property, object setTo)
{...}
public class A
{
public B B { get; set; } = new B();
}
public class B
{
public C C { get; set; } = new C();
}
public class C
{
public string D { get; set; } = "test";
}
它产生以下结果:
我要完成答案
public void SetProperty (object target, string property, object setTo)
{
var parts = property.Split ('.');
// if target object is List and target object no end target -
// we need cast to IList and get value by index
if (target.GetType ().Namespace == "System.Collections.Generic"
&& parts.Length != 1)
{
var targetList = (IList) target;
var value = targetList[int.Parse (parts.First ())];
SetProperty (value, string.Join (".", parts.Skip (1)), setTo);
}
else
{
var prop = target.GetType ().GetProperty (parts[0]);
if (parts.Length == 1)
{
// last property
prop.SetValue (target, setTo, null);
}
else
{
// Not at the end, go recursive
var value = prop.GetValue (target);
SetProperty (value, string.Join (".", parts.Skip (1)), setTo);
}
}
}
我想通过指定以点分隔的路径来更新任何 public 属性 中的值。
但是每当我调用我的方法时,我都会收到一个错误:
pi.SetValue(instance, value1, null);
错误信息:
对象与目标类型不匹配。
我的方法:
private void SetPathValue(object instance, string path, object value)
{
string[] pp = path.Split('.');
Type t = instance.GetType();
for (int i = 0; i < pp.Length; i++)
{
PropertyInfo pi = t.GetProperty(pp[i]);
if (pi == null)
{
throw new ArgumentException("Properties path is not correct");
}
else
{
instance = pi.GetValue(instance, null);
t = pi.PropertyType;
if (i == pp.Length - 1)//last
{
// Type targetType = IsNullableType(pi.PropertyType) ? Nullable.GetUnderlyingType(pi.PropertyType) : pi.PropertyType;
var value1 = Convert.ChangeType(value, instance.GetType());
pi.SetValue(instance, value1, null);//ERROR
}
}
}
}
private static bool IsNullableType(Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
我认为您的原始版本最终会设置值 "one level too deep"。
我认为递归模式更容易遵循,并且需要更少的代码。这是我拼凑的一个快速版本,适用于简单的测试用例。
有几个优化机会(在递归调用中重建字符串)和边缘情况(如 null
检查)我现在没有时间处理,但我没有不认为他们会太难添加。
public void SetProperty(object target, string property, object setTo)
{
var parts = property.Split('.');
var prop = target.GetType().GetProperty(parts[0]);
if (parts.Length == 1)
{
// last property
prop.SetValue(target, setTo, null);
}
else
{
// Not at the end, go recursive
var value = prop.GetValue(target);
SetProperty(value, string.Join(".", parts.Skip(1)), setTo);
}
}
Here 是一个 LINQPad 演示,展示了它的实际应用:
void Main()
{
var value = new A();
Debug.WriteLine("Original value:");
value.Dump();
Debug.WriteLine("Changed value:");
SetProperty(value, "B.C.D","changed!");
value.Dump();
}
public void SetProperty(object target, string property, object setTo)
{...}
public class A
{
public B B { get; set; } = new B();
}
public class B
{
public C C { get; set; } = new C();
}
public class C
{
public string D { get; set; } = "test";
}
它产生以下结果:
我要完成答案
public void SetProperty (object target, string property, object setTo)
{
var parts = property.Split ('.');
// if target object is List and target object no end target -
// we need cast to IList and get value by index
if (target.GetType ().Namespace == "System.Collections.Generic"
&& parts.Length != 1)
{
var targetList = (IList) target;
var value = targetList[int.Parse (parts.First ())];
SetProperty (value, string.Join (".", parts.Skip (1)), setTo);
}
else
{
var prop = target.GetType ().GetProperty (parts[0]);
if (parts.Length == 1)
{
// last property
prop.SetValue (target, setTo, null);
}
else
{
// Not at the end, go recursive
var value = prop.GetValue (target);
SetProperty (value, string.Join (".", parts.Skip (1)), setTo);
}
}
}