我尝试使用反射在obj1中设置Address1,但我无法弄清楚如何获取对正确对象的引用。我不确定如何获取对Address1实例的引用以传入SetValue()的第一个参数
第1轮:
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; }
}
}
根据Ed的反馈第2轮,仍然坚持如何获取此行的值: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; }
}
}
答案 0 :(得分:1)
如果您想使用反射从Addresses
的实例获取Person
属性的值,您可以这样做:
var myPerson = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var property = typeof(Person).GetProperty("Addresses");
var addresses = (IList<Address>) property.GetValue(myPerson );
首先,您要找到属性 - PropertyInfo
的实例 - 属于Person
类型。然后,您要为Person
,myPerson
的特定实例检索该属性的值。
addresses
是IList<Address>
,因此在使用反射从列表中获取特定Address
时没什么用处。但如果由于某种原因你想:
private Address GetAddressAtIndex(IList<Address> addresses, int index)
{
var property = typeof(IList<Address>).GetProperty("Item");
var address = (Address) property.GetValue(addresses, new object []{index});
return address;
}
这与第一个示例基本相同,只是在这种情况下属性(Item
)需要索引。所以我们使用接受一个或多个索引的GetValue
重载。
现在您已获得Address
的实例。我分别在不同的步骤中完成这些步骤,因为它们都是单独的步骤。没有一个步骤可以执行整个操作。
如果您有一个地址实例,并且想要使用反射来设置Address1
属性:
private void SetAddress1OnAddress(Address address, string address1Value)
{
var property = typeof(Address).GetProperty("Address1");
property.SetValue(address, address1Value);
}
非常相似。您首先检索Address1
属性,然后调用其SetValue
方法,以便在Address
时设置特定实例的值。
答案 1 :(得分:1)
这是我在LINQPad中放在一起的快速和脏,它改变了你定义的对象的Address1属性。
void Main()
{
var obj1 = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var index = 0;
var addressList = typeof(Person)
.GetProperty("Addresses")
.GetValue(obj1);
var address = addressList.GetType()
.GetProperty("Item")
.GetValue(addressList, new object[]{index});
address.GetType()
.GetProperty("Address1")
.SetValue(address,"321 Fake Street");
Console.WriteLine(obj1.Addresses[index].Address1); // Outputs 321 Fake Street
}
// Define other methods and classes here
public class Person
{
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}
答案 2 :(得分:1)
Type.GetGenericArguments()
没有像我认为的那样做任何事情。
你想要的是递归。给定”Foo.Bar[1].Baz”
,获取Foo
。从中获取Bar[1]
。从Baz
从其父级获取PropertyInfo,使用它来设置Baz
Bar[1]
属性Foo
属性的值。
要打破它:
编写一个“破解”属性名称并使用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);
}
}
这些方法都非常简单。既然你无论如何都在使用反射,你也可以全力以赴地编写一个非泛型方法来设置任何属性。