Лучший способ получить свойства sub с помощью GetProperty
public class Address
{
public string ZipCode {get; set;}
}
public class Customer
{
public Address Address {get; set;}
}
Как я могу получить доступ к eitther " 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, чтобы получить следующее значение в цепочке. Что-то вроде этого:--6-->
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");
обратите внимание, что это работает на этапе компиляции типы свойств. Если вы хотите он справляется с типом времени выполнения (например, если клиент.Адрес не имел свойства ZipCode, но фактический тип, возвращаемый адресом), затем измените property.PropertyType
to 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, при использовании obj.GetType () предоставит вам фактический тип экземпляра свойства.
EDIT: @Oliver-вы абсолютно правы, спасибо, что отметили это. Я скорректировал метод, чтобы учесть общие списки и словари. Пока Я не нравится часть разбора, я использовал умную идею Марка Гравелла в этой теме для получения значений свойства indexer.
существующие ответы прекрасны; просто альтернативная перспектива: во многих сценариях желательно использовать систему.ComponentModel, а не прямое отражение, поскольку это позволяет использовать сценарии свойств среды выполнения-т. е. как DataView DataTable предоставляет столбцы как свойства.
производительность - по умолчанию это в значительной степени идентично, но если вы делаете много этого (например, массовый импорт/экспорт данных), вы можете получить значительную производительность увеличивает использование этого подхода, любезно HyperDescriptor.
использовать системы.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;
}
список материалов примерно отражает поведение регулярной привязки данных (хотя он опускает несколько вещей, таких как контексты привязки, валютные менеджеры и т. д.)
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;
}
Проблема: Слабо Типизированные Переменные:
@jonskeet это(...) метод почти отвечал моим потребностям точно; за исключением этого моя собственность была напечатана слабо; следовательно,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;
}