【问题标题】:Best way to get sub properties using GetProperty使用 GetProperty 获取子属性的最佳方法
【发布时间】:2008-12-14 09:31:15
【问题描述】:
public class Address
{
    public string ZipCode {get; set;}
}

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

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

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

【问题讨论】:

    标签: c# reflection getproperty


    【解决方案1】:

    你需要类似的东西:

    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()

    另请注意,这没有任何错误处理等:)

    【讨论】:

      【解决方案2】:

      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<TKey, TValue>(IDictionary<TKey, TValue> dict, object index)
          {
              TKey key = (TKey)Convert.ChangeType(index, typeof(TKey), null);
              return dict[key];
          }
      
          public static T GetListElement<T>(IList<T> list, object index)
          {
              return list[Convert.ToInt32(index)];
          }
      
      }
      

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

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

      【讨论】:

      • 取决于您期望在路径中获得的内容,您将在索引属性上失败(例如somePerson.Adresses[3].Street
      【解决方案3】:

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

      性能方面 - 默认情况下,这在很大程度上是相同的,但如果您执行大量此操作(例如,批量数据导入/导出),您实际上可以使用这种方法获得显着的性能提升,由 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;
      }
      

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

      【讨论】:

        【解决方案4】:
        typeof (Customer).GetProperty("Address").PropertyType.GetProperty("ZipCode")
        

        【讨论】:

          【解决方案5】:

          阿达拜伦,

          当您只需要获取类型并且没有实际的对象实例时,我创建了您的代码版本。

              public static Type FollowPropertyPath<T>(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;
              }
          

          【讨论】:

            【解决方案6】:

            问题:弱类型变量:

            @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;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-12-08
              • 2023-03-06
              • 1970-01-01
              相关资源
              最近更新 更多