我正在尝试使用反射在 obj1 中设置 Address1,但我不知道如何获取对正确对象的引用。我不确定如何获取对 Address1 实例的引用以传递到 SetValue() 的第一个参数
第一轮:
public class StackOverflowReflectionTest
{
[Fact]
public void SetDeepPropertyUsingReflection()
{
var breadCrumb = ".Addresses[0].Address1";
var obj1 = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var newAddress1 = "123 Second Street";
var propNames = breadCrumb.Split(".");
for (var index = 0; index < propNames.Length; index++)
{
var propName = propNames[index];
if (propName.Contains("["))
{
var propNameToGet = propName.Substring(0, propName.IndexOf("[", StringComparison.Ordinal));
var prop = obj1.GetType().GetProperty(propNameToGet);
var leftBrace = propName.IndexOf("[", StringComparison.Ordinal);
var rightBrace = propName.IndexOf("]", StringComparison.Ordinal);
var position = int.Parse(propName.Substring(leftBrace + 1, rightBrace - leftBrace - 1));
var propNameToSet = propNames[index + 1];
var propToSet = prop.PropertyType.GetGenericArguments()[position].GetProperty(propNameToSet);
propToSet.SetValue(obj1, newAddress1);
}
else
{
//TODO: deal with different types
}
}
}
public class Person
{
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}
}
Round 2 根据Ed的反馈,仍然停留在如何获取这一行的值: var value = property.GetValue(obj, new object[] { indexPart });
public class StackOverflowReflectionTest
{
[Fact]
public void SetDeepPropertyUsingReflectionRound2()
{
var breadCrumb = "Addresses[0].Address1";
var obj1 = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var newAddress1 = "123 Second Street";
SetPropertyValueByPath(obj1, breadCrumb, newAddress1);
}
public bool CrackPropertyName(string name, out string namePart, out object indexPart)
{
if (name.Contains("["))
{
namePart = name.Substring(0, name.IndexOf("[", StringComparison.Ordinal));
var leftBrace = name.IndexOf("[", StringComparison.Ordinal);
var rightBrace = name.IndexOf("]", StringComparison.Ordinal);
indexPart = name.Substring(leftBrace + 1, rightBrace - leftBrace - 1);
return true;
}
else
{
namePart = name;
indexPart = null;
return false;
}
}
public object GetPropertyValue(object obj, string name)
{
if(CrackPropertyName(name, out var namePart, out var indexPart))
{
var property = obj.GetType().GetProperty(namePart);
var value = property.GetValue(obj, new object[] { indexPart });
return value;
}
else
{
return obj.GetType().GetProperty(name);
}
}
public void SetPropertyValue(object obj, string name, object newValue)
{
var property = typeof(Address).GetProperty(name);
property.SetValue(obj, newValue);
}
public void SetPropertyValueByPath(object obj, string path, object newValue)
{
var pathSegments = path.Split(".");
if (pathSegments.Length == 1)
{
SetPropertyValue(obj, pathSegments[0], newValue);
}
else
{
//// If more than one remaining segment, recurse
var child = GetPropertyValue(obj, pathSegments[0]);
SetPropertyValueByPath(child, String.Join(".", pathSegments.Skip(1)), newValue);
}
}
public class Person
{
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}
}
解决方案:
public class StackOverflowReflectionTest
{
[Fact]
public void SetDeepPropertyUsingReflectionSolution()
{
var breadCrumb = "Addresses[0].Address1";
var obj1 = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var newAddress1 = "123 Second Street";
SetPropertyValueByPath(obj1, breadCrumb, newAddress1);
}
public bool CrackPropertyName(string name, out string namePart, out object indexPart)
{
if (name.Contains("["))
{
namePart = name.Substring(0, name.IndexOf("[", StringComparison.Ordinal));
var leftBrace = name.IndexOf("[", StringComparison.Ordinal);
var rightBrace = name.IndexOf("]", StringComparison.Ordinal);
indexPart = name.Substring(leftBrace + 1, rightBrace - leftBrace - 1);
return true;
}
else
{
namePart = name;
indexPart = null;
return false;
}
}
public object GetPropertyValue(object obj, string name)
{
if(CrackPropertyName(name, out var namePart, out var indexPart))
{
var property = obj.GetType().GetProperty(namePart);
var list = property.GetValue(obj);
var value = list.GetType().GetProperty("Item").GetValue(list, new object[] { int.Parse(indexPart.ToString()) });
return value;
}
else
{
return obj.GetType().GetProperty(namePart);
}
}
public void SetPropertyValue(object obj, string name, object newValue)
{
var property = typeof(Address).GetProperty(name);
property.SetValue(obj, newValue);
}
public void SetPropertyValueByPath(object obj, string path, object newValue)
{
var pathSegments = path.Split(".");
if (pathSegments.Length == 1)
{
SetPropertyValue(obj, pathSegments[0], newValue);
}
else
{
//// If more than one remaining segment, recurse
var child = GetPropertyValue(obj, pathSegments[0]);
SetPropertyValueByPath(child, String.Join(".", pathSegments.Skip(1)), newValue);
}
}
public class Person
{
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}
}
最佳答案
Type.GetGenericArguments()
不会像我认为你假设的那样做任何事情。
你在这里想要的是递归。给定 ”Foo.Bar[1].Baz”
,得到 Foo
。从中获取 Bar[1]
。从其父级的 Baz
获取 PropertyInfo,使用它来设置 Bar[1]
属性的 Baz
属性的值 富
。
分解:
写一个“破解”一个属性名的方法,用out参数返回名称部分和索引值部分:
“IndexedProperty[1]”
进去; “IndexedProperty”和整数1
出来了。"FooBar"
进去,"FooBar"
和null
出来。如果有索引器则返回 true,否则返回 false。bool CrackPropertyName(string name, out string namePart, out object indexPart)
编写一个方法,它接受一个对象和一个字符串“PropertyName”或“IndexedPropety[0]”(不是路径 -- 没有点)并返回该对象的属性值。它使用
CrackPropertyName()
来简化其工作。object GetPropertyValue(object obj, string name)
编写一个按名称设置属性值的方法(不是按路径,只是按名称)。同样,它使用
CrackPropertyName()
来简化其工作。void SetPropertyValue(object obj, string name, object newValue)
使用上面的递归方法:
void SetPropertyValueByPath(object obj, string path, object newvalue) { var pathSegments = /* split path on '.' */; if (pathSegments.Length == 1) { SetPropertyValue(obj, pathSegments[0], newValue); } else { // If more than one remaining segment, recurse var child = GetNamedPropertyvalue(obj, pathSegments[0]); return SetPropertyValueByPath(obj, String.Join(".", pathSegments.Skip(1)), newValue); } }
这些方法都很简单。由于无论如何您都在使用反射,因此您不妨全力以赴并编写一个设置任何属性的非泛型方法。
关于c# - 使用反射在嵌套对象中设置属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48082626/