Biorąc pod uwagę następujące obiekty:
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; }
}
Chciałbym użyć refleksji, aby przejść przez, Invoice
aby uzyskać Name
właściwość a Customer
. Oto, czego szukam, zakładając, że ten kod zadziała:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Oczywiście kończy się to niepowodzeniem, ponieważ „BillTo.Address” nie jest prawidłową właściwością Invoice
klasy.
Spróbowałem więc napisać metodę dzielenia ciągu na części na kropce i przechodzenia po obiektach w poszukiwaniu interesującej mnie wartości końcowej. Działa dobrze, ale nie do końca mi to odpowiada:
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;
}
Jakieś pomysły na ulepszenie tej metody lub lepszy sposób rozwiązania tego problemu?
EDYTUJ po wysłaniu, widziałem kilka powiązanych postów ... Jednak nie wydaje się, aby istniała odpowiedź, która konkretnie odnosi się do tego pytania. Nadal chciałbym poznać opinie na temat mojej implementacji.
źródło
GetDesiredInvoice
zwraca ci obiekt typu,Invoice
dlaczego nie użyćinv.BillTo.Name
bezpośrednio?Odpowiedzi:
Właściwie myślę, że twoja logika jest w porządku. Osobiście prawdopodobnie zmieniłbym to tak, aby przekazać obiekt jako pierwszy parametr (który jest bardziej zgodny z PropertyInfo.GetValue, więc mniej zaskakujący).
Prawdopodobnie nazwałbym to również czymś bardziej podobnym do GetNestedPropertyValue, aby było oczywiste, że przeszukuje stos właściwości.
źródło
Object
klasie, co wzmacnia punkt dotyczący zmiany kolejności parametrów.Używam następującej metody, aby uzyskać wartości z (zagnieżdżonych klas) właściwości, takich jak
"Własność"
"Adres ulica"
„Adres.Kraj.Nazwa”
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; } }
Oto Fiddle: https://dotnetfiddle.net/PvKRH0
źródło
Wiem, że trochę się spóźniłem na imprezę i jak powiedzieli inni, twoja implementacja jest w porządku
... w prostych przypadkach użycia .
Jednak opracowałem bibliotekę, która rozwiązuje dokładnie ten przypadek użycia, Pather.CSharp .
Jest również dostępny jako pakiet Nuget .
Jej głównym klasa jest
Resolver
ze swojejResolve
metody.Przekazać mu przedmiot i na ścieżkę nieruchomości i zwróci pożądaną wartość .
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Address");
Ale może również rozwiązywać bardziej złożone ścieżki właściwości , w tym dostęp do tablicy i słownika.
Tak więc, na przykład, jeśli
Customer
miał wiele adresówpublic class Customer { public String Name { get; set; } public IEnumerable<String> Addresses { get; set; } }
możesz uzyskać dostęp do drugiego za pomocą
Addresses[1]
.Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Addresses[1]");
źródło
Musisz uzyskać dostęp do obiektu ACTUAL, nad którym musisz użyć refleksji. Oto o co mi chodzi:
Zamiast tego:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo.Address"); Object val = info.GetValue(inv, null);
Zrób to (edytowane na podstawie komentarza):
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);
Zobacz ten post, aby uzyskać więcej informacji: Używanie odbicia do ustawiania właściwości właściwości obiektu
źródło
W nadziei, że nie zabrzmią za późno na imprezę, chciałbym dodać moje rozwiązanie: Zdecydowanie użyj rekurencji w tej sytuacji
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(); } }
źródło
Nie wyjaśniasz źródła swojego „dyskomfortu”, ale zasadniczo wydaje mi się, że Twój kod jest zdrowy.
Jedyne, co bym kwestionował, to obsługa błędów. Zwracasz wartość null, jeśli kod próbuje przejść przez odwołanie o wartości null lub jeśli nazwa właściwości nie istnieje. To ukrywa błędy: trudno jest wiedzieć, czy zwróciło wartość null, ponieważ nie ma klienta BillTo, czy też dlatego, że błędnie wpisałeś go „BilTo.Address” ... lub ponieważ istnieje klient BillTo, a jego adres jest pusty! Pozwoliłbym metodzie na awarię i spalenie w takich przypadkach - po prostu pozwolę wyjątkowi uciec (lub może owinąć go w bardziej przyjazny).
źródło
Oto kolejna implementacja, która pominie zagnieżdżoną właściwość, jeśli jest to moduł wyliczający i będzie kontynuować głębszą pracę. Sprawdzanie wyliczenia nie wpływa na właściwości typu string.
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; } }
na podstawie tego pytania i dalej
Używam tego w projekcie MVC, aby dynamicznie porządkować moje dane, po prostu przekazując Właściwość do sortowania według przykładu:
result = result.OrderBy((s) => { return ReflectionMethods.GetPropValue("BookingItems.EventId", s); }).ToList();
gdzie BookingItems to lista obiektów.
źródło
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]); }
źródło
if (info == null) { /* throw exception instead*/ }
W rzeczywistości zgłosiłbym wyjątek, gdyby zażądali właściwości, która nie istnieje. Sposób, w jaki został zakodowany, jeśli wywołam GetPropValue i zwróci wartość null, nie wiem, czy to oznacza, że właściwość nie istniała, czy właściwość istniała, ale ma wartość null.
źródło
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() + "'"); }
źródło
Chciałem podzielić się moim rozwiązaniem, chociaż może być za późno. To rozwiązanie służy przede wszystkim do sprawdzenia, czy zagnieżdżona właściwość istnieje. Ale można go łatwo dostosować, aby w razie potrzeby zwrócić wartość właściwości.
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); } }
Musiałem odnieść się do kilku postów, aby wymyślić to rozwiązanie. Myślę, że to zadziała dla wielu zagnieżdżonych typów właściwości.
źródło
Moje połączenie internetowe było wyłączone, gdy musiałem rozwiązać ten sam problem, więc musiałem `` wymyślić koło na nowo '':
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); } }
Całkiem pewny, że to rozwiązuje problem dla dowolnego łańcucha używanego jako nazwa właściwości, niezależnie od stopnia zagnieżdżenia, o ile wszystko jest własnością.
źródło
Próbować
inv.GetType().GetProperty("BillTo+Address");
źródło