Angesichts der folgenden Objekte:
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; }
}
Ich würde gerne Reflexion verwenden, um durch das Invoice
zu gehen , um das Name
Eigentum von a zu erhalten Customer
. Ich gehe davon aus, dass dieser Code funktionieren würde:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Dies schlägt natürlich fehl, da "BillTo.Address" keine gültige Eigenschaft der Invoice
Klasse ist.
Also habe ich versucht, eine Methode zu schreiben, um die Zeichenfolge in der Periode in Teile zu teilen und die Objekte auf der Suche nach dem endgültigen Wert zu durchsuchen, an dem ich interessiert war. Es funktioniert in Ordnung, aber ich bin damit nicht ganz zufrieden:
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;
}
Irgendwelche Ideen, wie man diese Methode verbessern oder wie man dieses Problem besser lösen kann?
Nach dem Posten BEARBEITEN habe ich einige verwandte Beiträge gesehen ... Es scheint jedoch keine Antwort zu geben, die sich speziell mit dieser Frage befasst. Außerdem möchte ich immer noch das Feedback zu meiner Implementierung.
quelle
GetDesiredInvoice
Sie ein Objekt vom Typ zurückgeben,Invoice
warum nichtinv.BillTo.Name
direkt verwenden?Antworten:
Ich denke tatsächlich, dass Ihre Logik in Ordnung ist. Persönlich würde ich es wahrscheinlich ändern, damit Sie das Objekt als ersten Parameter übergeben (was eher mit PropertyInfo.GetValue übereinstimmt, also weniger überraschend).
Ich würde es wahrscheinlich auch eher GetNestedPropertyValue nennen, um deutlich zu machen, dass es den Eigenschaftenstapel durchsucht.
quelle
Object
Klasse gemacht, die den Punkt beim Neuordnen der Parameter verstärkt.Ich benutze die folgende Methode, um die Werte von (verschachtelten Klassen) Eigenschaften wie zu erhalten
"Eigentum"
"Address.Street"
"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; } }
Hier ist die Geige: https://dotnetfiddle.net/PvKRH0
quelle
Ich weiß, dass ich ein bisschen zu spät zur Party komme, und wie andere sagten, ist Ihre Implementierung in Ordnung
... für einfache Anwendungsfälle .
Ich habe jedoch eine Bibliothek entwickelt, die genau diesen Anwendungsfall löst: Pather.CSharp .
Es ist auch als Nuget-Paket erhältlich .
Seine Hauptklasse ist
Resolver
mit seinerResolve
Methode.Sie übergeben ihm ein Objekt und den Eigenschaftspfad und es wird der gewünschte Wert zurückgegeben .
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Address");
Es können jedoch auch komplexere Eigenschaftspfade aufgelöst werden , einschließlich Array- und Wörterbuchzugriff.
So zum Beispiel, wenn Sie
Customer
hat mehrere Adressenpublic class Customer { public String Name { get; set; } public IEnumerable<String> Addresses { get; set; } }
Sie können mit auf den zweiten zugreifen
Addresses[1]
.Invoice inv = GetDesiredInvoice(); // magic method to get an invoice var resolver = new Resolver(); object result = resolver.Resolve(inv, "BillTo.Addresses[1]");
quelle
Sie müssen auf das ACTUAL-Objekt zugreifen, für das Sie Reflection verwenden möchten. Folgendes meine ich:
An Stelle von:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice PropertyInfo info = inv.GetType().GetProperty("BillTo.Address"); Object val = info.GetValue(inv, null);
Tun Sie dies (basierend auf dem Kommentar bearbeitet):
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);
Weitere Informationen finden Sie in diesem Beitrag: Verwenden von Reflection zum Festlegen einer Eigenschaft einer Eigenschaft eines Objekts
quelle
In der Hoffnung, nicht zu spät für die Party zu klingen, möchte ich meine Lösung hinzufügen: Verwenden Sie in dieser Situation definitiv die Rekursion
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(); } }
quelle
Sie erklären nicht die Ursache Ihres "Unbehagens", aber Ihr Code sieht für mich im Grunde genommen gut aus.
Das einzige, was ich in Frage stellen würde, ist die Fehlerbehandlung. Sie geben null zurück, wenn der Code versucht, eine Nullreferenz zu durchlaufen, oder wenn der Eigenschaftsname nicht vorhanden ist. Dies verbirgt Fehler: Es ist schwer zu wissen, ob es null zurückgegeben hat, weil es keinen BillTo-Kunden gibt oder weil Sie es "BilTo.Address" falsch geschrieben haben ... oder weil es einen BillTo-Kunden gibt und seine Adresse null ist! Ich würde die Methode in diesen Fällen abstürzen und brennen lassen - lassen Sie einfach die Ausnahme entkommen (oder wickeln Sie sie in eine freundlichere ein).
quelle
Hier ist eine weitere Implementierung, die eine verschachtelte Eigenschaft überspringt, wenn es sich um einen Enumerator handelt, und tiefer geht. Die Eigenschaften der Typzeichenfolge werden von der Aufzählungsprüfung nicht beeinflusst.
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; } }
basierend auf dieser Frage und auf
Ich verwende dies in einem MVC-Projekt, um meine Daten dynamisch zu ordnen, indem ich einfach die Eigenschaft zum Sortieren nach Beispiel übergebe:
result = result.OrderBy((s) => { return ReflectionMethods.GetPropValue("BookingItems.EventId", s); }).ToList();
Dabei ist BookingItems eine Liste von Objekten.
quelle
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]); }
quelle
if (info == null) { /* throw exception instead*/ }
Ich würde tatsächlich eine Ausnahme auslösen, wenn sie eine Eigenschaft anfordern, die nicht existiert. So wie Sie es codiert haben, wenn ich GetPropValue aufrufe und es null zurückgibt, weiß ich nicht, ob dies bedeutet, dass die Eigenschaft nicht existiert hat oder die Eigenschaft existiert hat, aber ihr Wert war null.
quelle
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() + "'"); }
quelle
Meine Internetverbindung war unterbrochen, als ich das gleiche Problem lösen musste, also musste ich das Rad neu erfinden:
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); } }
Ziemlich sicher, dass dies das Problem für jede Zeichenfolge löst, die Sie für den Eigenschaftsnamen verwenden, unabhängig vom Ausmaß der Verschachtelung, solange alles eine Eigenschaft ist.
quelle
Ich wollte meine Lösung teilen, obwohl es möglicherweise zu spät ist. Diese Lösung besteht hauptsächlich darin, zu überprüfen, ob die verschachtelte Eigenschaft vorhanden ist. Es kann jedoch leicht angepasst werden, um bei Bedarf den Eigenschaftswert zurückzugeben.
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); } }
Ich musste mich auf einige Beiträge beziehen, um diese Lösung zu finden. Ich denke, dass dies für mehrere verschachtelte Eigenschaftstypen funktionieren wird.
quelle
Versuchen
inv.GetType().GetProperty("BillTo+Address");
quelle