在C#中使用reflection来获取嵌套对象的属性

鉴于以下对象:

public class Customer { public String Name { get; set; } public String Address { get; set; } } public class Invoice { public String ID { get; set; } public DateTime Date { get; set; } public Customer BillTo { get; set; } } 

我想使用reflection来通过Invoice获取CustomerName属性。 以下是我之后,假设此代码将工作:

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo.Address"); Object val = info.GetValue(inv, null); 

当然,由于“BillTo.Address”不是Invoice类的有效属性,因此失败。

所以,我试着写一个方法来把这个string分割成几段,然后把这些对象放到我感兴趣的最后的值中去。它可以正常工作,但我并不完全适应它:

 public Object GetPropValue(String name, Object obj) { foreach (String part in name.Split('.')) { if (obj == null) { return null; } Type type = obj.GetType(); PropertyInfo info = type.GetProperty(part); if (info == null) { return null; } obj = info.GetValue(obj, null); } return obj; } 

有关如何改进这种方法的任何想法,或更好的方法来解决这个问题?

编辑发布后,我看到了一些相关的post…然而,似乎没有专门解决这个问题的答案。 另外,我仍然喜欢我的实施反馈。

我其实觉得你的逻辑很好 就个人而言,我可能会改变它,所以你传递的对象作为第一个参数(这是更内联PropertyInfo.GetValue,所以不那么令人惊讶)。

我也可能会把它称为更像GetNestedPropertyValue,以显而易见,它search属性堆栈。

您必须访问您需要使用reflection的ACTUAL对象。 这是我的意思:

而不是这个:

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo.Address"); Object val = info.GetValue(inv, null); 

做到这一点(根据评论编辑):

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo"); Customer cust = (Customer)info.GetValue(inv, null); PropertyInfo info2 = cust.GetType().GetProperty("Address"); Object val = info2.GetValue(cust, null); 

看看这篇文章的更多信息: 使用reflection来设置一个对象的属性的属性

你不解释你的“不适”的来源,但你的代码基本上听起来对我来说。

我唯一会问的是error handling。 如果代码尝试遍历空引用或者属性名称不存在,则返回null。 这隐藏了错误:很难知道它是否返回null,因为没有BillTo客户,或者因为拼错它“BilTo.Address”…或者因为有一个BillTo客户,而且它的地址是空的! 我会让这个方法在这些情况下崩溃并烧毁 – 只是让exception逃逸(或者把它包装在一个更友好的例子中)。

我知道我晚了一点,正如其他人所说, 你的实施是好的
简单的用例
但是,我开发了一个库,可以完全解决这个用例, Pather.CSharp 。
它也可以作为Nuget包装 。

它的主要类是ResolverResolve方法。
你传递一个对象和 属性path ,它会返回所需的值

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Address"); 

但它也可以解决更复杂的属性path ,包括数组和字典访问。
所以,例如,如果你的Customer多个地址

 public class Customer { public String Name { get; set; } public IEnumerable<String> Addresses { get; set; } } 

你可以使用Addresses[1]访问第二个。

 Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Addresses[1]"); 

希望不要太晚,我想添加我的解决scheme:在这种情况下绝对使用recursion

 public static Object GetPropValue(String name, object obj, Type type) { var parts = name.Split('.').ToList(); var currentPart = parts[0]; PropertyInfo info = type.GetProperty(currentPart); if (info == null) { return null; } if (name.IndexOf(".") > -1) { parts.Remove(currentPart); return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType); } else { return info.GetValue(obj, null).ToString(); } } 

我使用这种方法来获取像属性的值

“属性”

“Address.Street”

“Address.Country.Name”

  public static object GetPropertyValue(object src, string propName) { if (src == null) throw new ArgumentException("Value cannot be null.", "src"); if (propName == null) throw new ArgumentException("Value cannot be null.", "propName"); if(propName.Contains("."))//complex type nested { var temp = propName.Split(new char[] { '.' }, 2); return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]); } else { var prop = src.GetType().GetProperty(propName); return prop != null ? prop.GetValue(src, null) : null; } } 

这里的小提琴: https : //dotnetfiddle.net/PvKRH0

 > Get Nest properties eg, Developer.Project.Name 
 private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName) { if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0) throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString())); if (PropertName.Split('.').Length == 1) return t.GetType().GetProperty(PropertName); else return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]); } 
  if (info == null) { /* throw exception instead*/ } 

如果他们请求一个不存在的属性,我实际上会抛出一个exception。 你有它的方式编码,如果我调用GetPropValue,它返回null,我不知道是否意味着该属性不存在,或属性确实存在,但它的值为null。

  public static string GetObjectPropertyValue(object obj, string propertyName) { bool propertyHasDot = propertyName.IndexOf(".") > -1; string firstPartBeforeDot; string nextParts = ""; if (!propertyHasDot) firstPartBeforeDot = propertyName.ToLower(); else { firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower(); nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1); } foreach (var property in obj.GetType().GetProperties()) if (property.Name.ToLower() == firstPartBeforeDot) if (!propertyHasDot) if (property.GetValue(obj, null) != null) return property.GetValue(obj, null).ToString(); else return DefaultValue(property.GetValue(obj, null), propertyName).ToString(); else return GetObjectPropertyValue(property.GetValue(obj, null), nextParts); throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'"); } 

这是另一个实现,如果它是一个枚举器,将跳过一个嵌套的属性,并继续深入。 枚举检查不影响stringtypes的属性。

 public static class ReflectionMethods { public static bool IsNonStringEnumerable(this PropertyInfo pi) { return pi != null && pi.PropertyType.IsNonStringEnumerable(); } public static bool IsNonStringEnumerable(this object instance) { return instance != null && instance.GetType().IsNonStringEnumerable(); } public static bool IsNonStringEnumerable(this Type type) { if (type == null || type == typeof(string)) return false; return typeof(IEnumerable).IsAssignableFrom(type); } public static Object GetPropValue(String name, Object obj) { foreach (String part in name.Split('.')) { if (obj == null) { return null; } if (obj.IsNonStringEnumerable()) { var toEnumerable = (IEnumerable)obj; var iterator = toEnumerable.GetEnumerator(); if (!iterator.MoveNext()) { return null; } obj = iterator.Current; } Type type = obj.GetType(); PropertyInfo info = type.GetProperty(part); if (info == null) { return null; } obj = info.GetValue(obj, null); } return obj; } } 

基于这个问题和

如何知道一个PropertyInfo是否由Berryl 收集

我在一个MVC项目中使用这个来dynamic地sorting我的数据,只需通过传递属性来按例sorting

 result = result.OrderBy((s) => { return ReflectionMethods.GetPropValue("BookingItems.EventId", s); }).ToList(); 

其中BookingItems是对象列表。

当我需要解决同样的问题时,我的networking连接断开了,所以我不得不重新发明轮子:

 static object GetPropertyValue(Object fromObject, string propertyName) { Type objectType = fromObject.GetType(); PropertyInfo propInfo = objectType.GetProperty(propertyName); if (propInfo == null && propertyName.Contains('.')) { string firstProp = propertyName.Substring(0, propertyName.IndexOf('.')); propInfo = objectType.GetProperty(firstProp); if (propInfo == null)//property name is invalid { throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString())); } return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1)); } else { return propInfo.GetValue(fromObject, null); } } 

相当肯定,这可以解决任何你使用的属性名称的string的问题,无论嵌套的程度如何,只要一切都是属性。

尝试inv.GetType().GetProperty("BillTo+Address");