2008-12-14 5 views

ответ

41

Вам потребуется что-то вроде:

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"); 

Обратите внимание, что это работает на типах во время компиляции свойств. Если вы хотите, чтобы он справлялся с типом времени выполнения (например, если у клиента.Address не было свойства ZipCode, но фактический тип, возвращаемый адресом, то), то измените property.PropertyType на property.GetType().

Также обратите внимание, что это не имеет никакого обработку ошибок и т.д. :)

+0

Хороший ответ! ... Суперподобный. – Hrishi 2014-04-23 10:39:19

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

Существующие ответы в порядке; просто альтернативная перспектива: во многих сценариях желательно использовать System.ComponentModel, а не прямое отражение, поскольку это позволяет использовать сценарии свойств времени выполнения, то есть как DataView 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» (сглаживание некоторых деталей, таких как списки и т. д.).

(обратите внимание, что вы можете бросить молнию в виде строки и т.д., если вы знаете, что ожидаемый тип)

Чтобы получить значение из глубокого пути (в том числе и тот же список обработки, который использует привязки данных), вы бы использовать что-то вроде:

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

списка вещь примерно отражает поведение регулярного связывание данных (хотя он не включает несколько вещей, как обязательные контексты, валютные-менеджеры и т.д.)

6

Jon Скит Ответ хорошо, я должен был расширить свой метод немного, хотя, для того, чтобы учесть полученные экземпляры в пределах пути свойства:

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(), вы получите фактический тип экземпляра свойства.

EDIT: @Oliver - вы абсолютно правы, спасибо, что заметили это. Я скорректировал метод, чтобы разрешить общие списки и словари. Хотя мне не нравится парсинг, я использовал умную идею Марка Гравелла в this thread, чтобы получить значения свойства indexer.

+1

в зависимости от того, что вы ожидаете получить в пути, вы потерпите неудачу при индексированных свойствах (например, `somePerson.Adresses [3] .Street`) – Oliver 2011-02-14 10:15:03

1

adabyron,

Я создал версию кода для того, когда вам нужно только, чтобы захватить типы, и если у вас нет фактического экземпляра объекта.

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