Учитывая следующие объекты:
public class Customer {
public String Name { get; set; }
public String Address { get; set; }
}
public class Invoice {
public String ID { get; set; }
public DateTime Date { get; set; }
public Customer BillTo { get; set; }
}
Я хотел бы использовать отражение, чтобы пройти через, Invoice
чтобы получить Name
свойство Customer
. Вот что мне нужно, если этот код будет работать:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Конечно, это не удается, поскольку BillTo.Address не является допустимым свойством Invoice
класса.
Итак, я попытался написать метод, чтобы разбить строку на части по периоду и пройтись по объектам в поисках окончательного значения, которое меня интересовало. Он работает нормально, но мне это не совсем удобно:
public Object GetPropValue(String name, Object obj) {
foreach (String part in name.Split('.')) {
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
Есть идеи, как улучшить этот метод или как лучше решить эту проблему?
ИЗМЕНИТЬ после публикации, я увидел несколько связанных сообщений ... Однако, похоже, нет ответа, который конкретно касается этого вопроса. Также мне хотелось бы получить отзывы о моей реализации.
c#
reflection
джеддинги
источник
источник
GetDesiredInvoice
вернете вам объект типа,Invoice
почему бы не использоватьinv.BillTo.Name
напрямую?Ответы:
Я действительно думаю, что ваша логика в порядке. Лично я бы, вероятно, изменил его так, чтобы вы передавали объект в качестве первого параметра (что более похоже на PropertyInfo.GetValue, поэтому менее удивительно).
Я бы также, вероятно, назвал это чем-то вроде GetNestedPropertyValue, чтобы было очевидно, что он выполняет поиск по стеку свойств.
источник
Object
класса, что усиливает точку зрения на переупорядочение параметров.Я использую следующий метод для получения значений из свойств (вложенных классов), например
"Свойство"
"Адрес улицы"
"Address.Country.Name"
public static object GetPropertyValue(object src, string propName) { if (src == null) throw new ArgumentException("Value cannot be null.", "src"); if (propName == null) throw new ArgumentException("Value cannot be null.", "propName"); if(propName.Contains("."))//complex type nested { var temp = propName.Split(new char[] { '.' }, 2); return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]); } else { var prop = src.GetType().GetProperty(propName); return prop != null ? prop.GetValue(src, null) : null; } }
Вот скрипка: https://dotnetfiddle.net/PvKRH0
источник
Я знаю, что немного опаздываю на вечеринку, и, как говорили другие, ваша реализация в порядке
... для простых случаев использования .
Однако я разработал библиотеку, которая решает именно этот вариант использования, Pather.CSharp .
Он также доступен как пакет Nuget .
Его основной класс -
Resolver
этоResolve
метод.Вы передаете ему объект и путь к свойству , и он возвращает желаемое значение .
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Address");
Но он также может разрешать более сложные пути свойств , включая доступ к массиву и словарю.
Так, например, если у вас
Customer
было несколько адресовpublic class Customer { public String Name { get; set; } public IEnumerable<String> Addresses { get; set; } }
вы можете получить доступ ко второму, используя
Addresses[1]
.Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Addresses[1]");
источник
Вы должны получить доступ к ФАКТИЧЕСКОМУ объекту, для которого нужно использовать отражение. Вот что я имею в виду:
Вместо этого:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo.Address"); Object val = info.GetValue(inv, null);
Сделайте это (отредактировано на основе комментария):
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo"); Customer cust = (Customer)info.GetValue(inv, null); PropertyInfo info2 = cust.GetType().GetProperty("Address"); Object val = info2.GetValue(cust, null);
Посмотрите этот пост для получения дополнительной информации: Использование отражения для установки свойства свойства объекта
источник
В надежде, что я не покажусь слишком поздно для вечеринки, я хотел бы добавить свое решение: обязательно используйте рекурсию в этой ситуации
public static Object GetPropValue(String name, object obj, Type type) { var parts = name.Split('.').ToList(); var currentPart = parts[0]; PropertyInfo info = type.GetProperty(currentPart); if (info == null) { return null; } if (name.IndexOf(".") > -1) { parts.Remove(currentPart); return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType); } else { return info.GetValue(obj, null).ToString(); } }
источник
Вы не объясняете источник своего «дискомфорта», но ваш код в основном кажется мне разумным.
Единственное, что меня интересует, - это обработка ошибок. Вы возвращаете null, если код пытается пройти через пустую ссылку или если имя свойства не существует. Это скрывает ошибки: трудно понять, вернул ли он значение null, потому что нет клиента BillTo, или из-за того, что вы неправильно написали "BilTo.Address" ... или потому, что есть клиент BillTo, и его адрес равен нулю! В таких случаях я бы позволил методу вылететь и сгореть - просто позволю исключению ускользнуть (или, возможно, обернуть его более дружелюбным).
источник
Вот еще одна реализация, которая пропустит вложенное свойство, если это перечислитель, и продолжит глубже. Проверка перечисления не влияет на свойства строки типа.
public static class ReflectionMethods { public static bool IsNonStringEnumerable(this PropertyInfo pi) { return pi != null && pi.PropertyType.IsNonStringEnumerable(); } public static bool IsNonStringEnumerable(this object instance) { return instance != null && instance.GetType().IsNonStringEnumerable(); } public static bool IsNonStringEnumerable(this Type type) { if (type == null || type == typeof(string)) return false; return typeof(IEnumerable).IsAssignableFrom(type); } public static Object GetPropValue(String name, Object obj) { foreach (String part in name.Split('.')) { if (obj == null) { return null; } if (obj.IsNonStringEnumerable()) { var toEnumerable = (IEnumerable)obj; var iterator = toEnumerable.GetEnumerator(); if (!iterator.MoveNext()) { return null; } obj = iterator.Current; } Type type = obj.GetType(); PropertyInfo info = type.GetProperty(part); if (info == null) { return null; } obj = info.GetValue(obj, null); } return obj; } }
на основе этого вопроса и на
Я использую это в проекте MVC, чтобы динамически упорядочивать мои данные, просто передав свойство для сортировки по примеру:
result = result.OrderBy((s) => { return ReflectionMethods.GetPropValue("BookingItems.EventId", s); }).ToList();
где BookingItems - список объектов.
источник
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName) { if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0) throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString())); if (PropertName.Split('.').Length == 1) return t.GetType().GetProperty(PropertName); else return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]); }
источник
if (info == null) { /* throw exception instead*/ }
Я бы фактически выбросил исключение, если бы они запросили несуществующее свойство. Как вы это закодировали, если я вызываю GetPropValue, и он возвращает null, я не знаю, означает ли это, что свойство не существует, или свойство действительно существует, но его значение было нулевым.
источник
public static string GetObjectPropertyValue(object obj, string propertyName) { bool propertyHasDot = propertyName.IndexOf(".") > -1; string firstPartBeforeDot; string nextParts = ""; if (!propertyHasDot) firstPartBeforeDot = propertyName.ToLower(); else { firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower(); nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1); } foreach (var property in obj.GetType().GetProperties()) if (property.Name.ToLower() == firstPartBeforeDot) if (!propertyHasDot) if (property.GetValue(obj, null) != null) return property.GetValue(obj, null).ToString(); else return DefaultValue(property.GetValue(obj, null), propertyName).ToString(); else return GetObjectPropertyValue(property.GetValue(obj, null), nextParts); throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'"); }
источник
Я хотел поделиться своим решением, хотя может быть уже слишком поздно. Это решение в первую очередь предназначено для проверки наличия вложенного свойства. Но его можно легко настроить, чтобы при необходимости возвращать значение свойства.
private static PropertyInfo _GetPropertyInfo(Type type, string propertyName) { //*** //*** Check if the property name is a complex nested type //*** if (propertyName.Contains(".")) { //*** //*** Get the first property name of the complex type //*** var tempPropertyName = propertyName.Split(".", 2); //*** //*** Check if the property exists in the type //*** var prop = _GetPropertyInfo(type, tempPropertyName[0]); if (prop != null) { //*** //*** Drill down to check if the nested property exists in the complex type //*** return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]); } else { return null; } } else { return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); } }
Мне пришлось обратиться к нескольким сообщениям, чтобы придумать это решение. Я думаю, это сработает для нескольких типов вложенных свойств.
источник
Мое интернет-соединение было отключено, когда мне нужно было решить ту же проблему, поэтому мне пришлось «заново изобрести колесо»:
static object GetPropertyValue(Object fromObject, string propertyName) { Type objectType = fromObject.GetType(); PropertyInfo propInfo = objectType.GetProperty(propertyName); if (propInfo == null && propertyName.Contains('.')) { string firstProp = propertyName.Substring(0, propertyName.IndexOf('.')); propInfo = objectType.GetProperty(firstProp); if (propInfo == null)//property name is invalid { throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString())); } return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1)); } else { return propInfo.GetValue(fromObject, null); } }
Совершенно уверен, что это решает проблему для любой строки, которую вы используете для имени свойства, независимо от степени вложенности, пока все является свойством.
источник
Пытаться
inv.GetType().GetProperty("BillTo+Address");
источник