使用GetProperty获取子属性的最佳方法

public class Address { public string ZipCode {get; set;} } public class Customer { public Address Address {get; set;} } 

如何通过reflection访问“ZipCode”或“Address.ZipCode”? 例如:

 Typeof(Customer).GetProperty("ZipCode")? 

你需要这样的东西:

 PropertyInfo addressProperty = typeof(Customer).GetProperty("Address"); ProportyInfo zipCodeProperty = addressProperty.PropertyType.GetProperty("ZipCode"); object address = addressProperty.GetValue(customer, null); object zipCode = zipCodeProperty.GetValue(address, null); 

基本上,如果你想取一个字符串“Address.ZipCode”并向下导航,你需要将它拆分为“。” 然后在每一步调用适当类型的GetProperty以获取属性本身,然后使用PropertyInfo.GetValue获取链中的下一个值。 像这样的东西:

 public static object FollowPropertyPath(object value, string path) { Type currentType = value.GetType(); foreach (string propertyName in path.Split('.')) { PropertyInfo property = currentType.GetProperty(propertyName); value = property.GetValue(value, null); currentType = property.PropertyType; } return value; } 

这样叫:

 object zipCode = FollowPropertyPath(customer, "Address.ZipCode"); 

请注意,这适用于属性的编译时类型。 如果您希望它处理执行时间类型(例如,如果customer.Address没有ZipCode属性,但是Address返回的实际类型),则将property.PropertyType更改为property.GetType()

另请注意,这没有任何error handling等:)

Jon Skeet的答案很好,我不得不扩展他的方法,以便考虑属性路径中的派生实例:

 public static class ReflectorUtil { public static object FollowPropertyPath(object value, string path) { if (value == null) throw new ArgumentNullException("value"); if (path == null) throw new ArgumentNullException("path"); Type currentType = value.GetType(); object obj = value; foreach (string propertyName in path.Split('.')) { if (currentType != null) { PropertyInfo property = null; int brackStart = propertyName.IndexOf("["); int brackEnd = propertyName.IndexOf("]"); property = currentType.GetProperty(brackStart > 0 ? propertyName.Substring(0, brackStart) : propertyName); obj = property.GetValue(obj, null); if (brackStart > 0) { string index = propertyName.Substring(brackStart + 1, brackEnd - brackStart - 1); foreach (Type iType in obj.GetType().GetInterfaces()) { if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) { obj = typeof(ReflectorUtil).GetMethod("GetDictionaryElement") .MakeGenericMethod(iType.GetGenericArguments()) .Invoke(null, new object[] { obj, index }); break; } if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof(IList<>)) { obj = typeof(ReflectorUtil).GetMethod("GetListElement") .MakeGenericMethod(iType.GetGenericArguments()) .Invoke(null, new object[] { obj, index }); break; } } } currentType = obj != null ? obj.GetType() : null; //property.PropertyType; } else return null; } return obj; } public static TValue GetDictionaryElement(IDictionary dict, object index) { TKey key = (TKey)Convert.ChangeType(index, typeof(TKey), null); return dict[key]; } public static T GetListElement(IList list, object index) { return list[Convert.ToInt32(index)]; } } 

使用property.PropertyType将获得在obj类上定义的属性类型,而使用obj.GetType()将获得属性实例的实际类型。

编辑:@Oliver – 你是绝对正确的,谢谢你的注意。 我调整了方法以允许通用列表和字典。 虽然我不喜欢解析部分,但我在这个线程中使用了Marc Gravell的聪明主意来获取索引器属性的值。

现有答案很好; 只是另一种观点:在许多场景中,最好使用System.ComponentModel而不是直接reflection,因为这允许运行时属性场景 – 即DataTable的DataView如何将列作为属性公开。

性能方面 – 默认情况下,这在很大程度上是相同的,但如果你正在做很多这样的事情(例如,批量数据导入/导出),使用这种方法实际上可以获得显着的性能提升,这得益于HyperDescriptor 。

要使用System.ComponentModel,代码类似,但略有不同:

 static void Main() { object obj = new Customer { Address = new Address { ZipCode = "abcdef" } }; object address = GetValue(obj, "Address"); object zip = GetValue(address, "ZipCode"); Console.WriteLine(zip); } static object GetValue(object component, string propertyName) { return TypeDescriptor.GetProperties(component)[propertyName].GetValue(component); } 

然后,这将为您提供相同的处理,就像您使用数据绑定绑定到“Address.ZipCode”(对列表等的某些细节进行掩饰)。

(请注意,如果您知道这是预期的类型,您可以将zip转换为字符串等)

要从深层路径获取值(包括处理数据绑定使用的相同列表),您可以使用以下内容:

 static object ResolveValue(object component, string path) { foreach(string segment in path.Split('.')) { if (component == null) return null; if(component is IListSource) { component = ((IListSource)component).GetList(); } if (component is IList) { component = ((IList)component)[0]; } component = GetValue(component, segment); } return component; } 

列表内容大致反映了常规数据绑定的行为(尽管它省略了一些事情,如绑定上下文,货币管理器等)

 typeof (Customer).GetProperty("Address").PropertyType.GetProperty("ZipCode") 

adabyron,

我创建了一个代码版本,当你只需要获取类型时,如果你没有实际的对象实例。

  public static Type FollowPropertyPath(string path) { if (path == null) throw new ArgumentNullException("path"); Type currentType = typeof(T); foreach (string propertyName in path.Split('.')) { int brackStart = propertyName.IndexOf("["); var property = currentType.GetProperty(brackStart > 0 ? propertyName.Substring(0, brackStart) : propertyName); if (property == null) return null; currentType = property.PropertyType; if (brackStart > 0) { foreach (Type iType in currentType.GetInterfaces()) { if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof (IDictionary<,>)) { currentType = iType.GetGenericArguments()[1]; break; } if (iType.IsGenericType && iType.GetGenericTypeDefinition() == typeof (ICollection<>)) { currentType = iType.GetGenericArguments()[0]; break; } } } } return currentType; } 

问题:弱类型变量:

@ jonskeet的FollowPropertyPath(…)方法几乎完全满足了我的需求; 除了我的财产是弱的类型; 因此, currentType = property.PropertyType只返回System.Object,并在foreach循环的下一次迭代中失败。

解决方案:要使用运行时类型而不是设计时类型,我按如下方式调整了方法:

 public static object FollowPropertyPath(object value, string path) { Type currentType = value.GetType(); foreach (string propertyName in path.Split('.')) { PropertyInfo property = currentType.GetProperty(propertyName); value = property.GetValue(value, null); currentType = value.GetType(); // <-- Change } return value; }