Лучший способ получить дополнительные свойства с помощью GetProperty
public class Address
{
public string ZipCode {get; set;}
}
public class Customer
{
public Address Address {get; set;}
}
Как я могу получить доступ к "ZipCode"или" Address.Индекс" с отражением? Например:
Typeof(Customer).GetProperty("ZipCode")?
6 ответов:
Вам понадобится что-то вроде:
PropertyInfo addressProperty = typeof(Customer).GetProperty("Address"); ProportyInfo zipCodeProperty = addressProperty.PropertyType.GetProperty("ZipCode"); object address = addressProperty.GetValue(customer, null); object zipCode = zipCodeProperty.GetValue(address, null);В основном, если вы хотите взять строку " адрес.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; }Назовем это так:
Обратите внимание, что это работает с типами свойств во время компиляции. Если вы хотите, чтобы он справился с исполнением тип времени (например, если клиент.Address не имеет свойства ZipCode, но фактический тип, возвращаемый Address, имеет), затем изменитеobject zipCode = FollowPropertyPath(customer, "Address.ZipCode");property.PropertyTypeнаproperty.GetType().Также обратите внимание, что это не имеет никакой обработки ошибок и т.д.:)
Ответ Джона Скита прекрасен, хотя мне пришлось немного расширить его метод, чтобы учесть производные экземпляры в пути свойств:
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)]; } }Использование свойства.PropertyType будет Вам тип недвижимости определена на объект класса, в то время как использование obj.Функция GetType () выдаст вам фактический тип экземпляра свойства.
EDIT: @Oliver-вы абсолютно правы, спасибо, что отметили это. Я скорректировал метод, чтобы учесть общие списки и словари. Хотя мне это не нравится. разбирая часть, я использовал умную идею Марка Гравелла в этой нити, чтобы получить значения свойства indexer.
Существующие ответы прекрасны; просто альтернативная перспектива: во многих сценариях желательно использовать систему.ComponentModel, а не прямое отражение, так как это позволяет создавать сценарии свойств среды выполнения - например, как DataView объекта DataTable предоставляет столбцы в виде свойств.
Performance wise-по умолчанию это в основном идентично, но если вы делаете много этого (например, массовый импорт/экспорт данных), вы действительно можете получить значительное увеличение производительности с помощью этого подход, любезно предоставленный Гипердескриптором .
Использовать систему.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); }Это дает вам такую же обработку, как если бы вы использовали привязку данных для привязки к "адресу.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; }Список вещей грубо отражает поведение регулярной привязки данных (хотя он опускает некоторые вещи, такие как контексты привязки, валютные менеджеры и т. д.)
Адабирон,
Я создал версию вашего кода для тех случаев, когда вам нужно только захватить типы, и если у вас нет реального экземпляра объекта.
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; }
Проблема: Слабо Типизированные Переменные:
@ jonskeet's FollowPropertyPath(...) метод почти точно отвечал моим потребностям; за исключением того, что моя собственность была слабой типизацией; следовательно,
currentType = property.PropertyTypeпросто возвращена системы.Объект и сбой на следующей итерации цикла 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; }
Comments