Wie greife ich in C # auf eine anonyme Eigenschaft zu?
125
Ich habe das:
List<object> nodes =newList<object>();
nodes.Add(new{Checked=false,
depth =1,
id ="div_"+ d.Id});
... und ich frage mich, ob ich dann die "Checked" -Eigenschaft des anonymen Objekts abrufen kann. Ich bin mir nicht sicher, ob das überhaupt möglich ist. Versucht dies zu tun:
if (nodes.Any(n => n["Checked"] == false)) ... aber es funktioniert nicht.
Wenn Sie eine stark typisierte Liste anonymer Typen wünschen, müssen Sie die Liste auch zu einem anonymen Typ machen. Der einfachste Weg, dies zu tun, besteht darin, eine Sequenz wie ein Array in eine Liste zu projizieren, z
var nodes =(new[]{new{Checked=false,/* etc */}}).ToList();
Dann können Sie wie folgt darauf zugreifen:
nodes.Any(n => n.Checked);
Aufgrund der Funktionsweise des Compilers sollte das Folgende auch funktionieren, sobald Sie die Liste erstellt haben, da die anonymen Typen dieselbe Struktur haben und daher auch denselben Typ haben. Ich habe jedoch keinen Compiler zur Hand, um dies zu überprüfen.
Wenn Sie das Objekt als Typ speichern object, müssen Sie Reflektion verwenden. Dies gilt für jeden anonymen oder sonstigen Objekttyp. Auf einem Objekt o können Sie seinen Typ erhalten:
Type t = o.GetType();
Dann suchen Sie eine Eigenschaft:
PropertyInfo p = t.GetProperty("Foo");
Daraus können Sie dann einen Wert erhalten:
object v = p.GetValue(o,null);
Diese Antwort ist für ein Update für C # 4 längst überfällig:
dynamic d = o;object v = d.Foo;
Und jetzt noch eine Alternative in C # 6:
object v = o?.GetType().GetProperty("Foo")?.GetValue(o,null);
Beachten Sie, dass durch die Verwendung ?.wir bewirken , dass die resultierenden vzu sein nullin drei verschiedenen Situationen!
oist null, also gibt es überhaupt kein Objekt
oist nicht null, hat aber keine EigenschaftFoo
ohat eine Eigenschaft, Fooaber ihr wirklicher Wert ist zufällig null.
Dies entspricht also nicht den früheren Beispielen, kann jedoch sinnvoll sein, wenn Sie alle drei Fälle gleich behandeln möchten.
Bis jetzt noch nie eine Dynamik verwendet, schönes Update für .NET 4.0
Alan
In der c # 4-Lösung erhalten Sie eine Laufzeitausnahme, wenn die Eigenschaft nicht vorhanden ist ( object v = d.Foo), während GetValue(o, null)sie null ist, wenn sie nicht vorhanden ist.
YaakovHatam
1
Nein, GetPropertywird zurückkehren nullund GetValuewerfen, wenn dies bestanden nullwird. Der Gesamteffekt ist also eine Ausnahme. Die C # 4.0-Version bietet eine aussagekräftigere Ausnahme.
Daniel Earwicker
4
Wenn Sie Dynamic in einer anderen Assembly als der Quelle verwenden, müssen Sie [InternalsVisibleTo]
Sarath
2
@ DanielEarwicker danke für die Fertigstellung. Dies gilt auch für anonyme Typen. Da alle für anonyme Typen generierten Eigenschaften intern sind.
Sarath
13
Sie können die Eigenschaften des anonymen Typs mithilfe von Reflection durchlaufen. Überprüfen Sie, ob eine "Checked" -Eigenschaft vorhanden ist, und ermitteln Sie deren Wert.
foreach(object o in nodes){Type t = o.GetType();PropertyInfo[] pi = t.GetProperties();foreach(PropertyInfo p in pi){if(p.Name=="Checked"&&!(bool)p.GetValue(o))Console.WriteLine("awesome!");}}
Wenn Sie nur eine Eigenschaft benötigen und deren Namen bereits kennen, macht es keinen Sinn, alle durchzugehen. Verwenden Sie einfach GetProperty und GetValue. Außerdem ist System.out.println Java, nicht C # ...
Chris Charabaruk
Ups, so ist es, Chris! Ein bisschen peinlich ... jetzt behoben.
Glennkentwell
6
Die akzeptierte Antwort beschreibt korrekt, wie die Liste deklariert werden soll, und wird für die meisten Szenarien dringend empfohlen.
Ich bin jedoch auf ein anderes Szenario gestoßen, das auch die gestellte Frage abdeckt. Was ist, wenn Sie eine vorhandene Objektliste wie ViewData["htmlAttributes"]in MVC verwenden müssen ? Wie können Sie auf seine Eigenschaften zugreifen (sie werden normalerweise über erstellt new { @style="width: 100px", ... })?
Für dieses etwas andere Szenario möchte ich Ihnen mitteilen, was ich herausgefunden habe. In den folgenden Lösungen gehe ich von folgender Erklärung aus für nodes:
List<object> nodes =newList<object>();
nodes.Add(new{Checked=false,
depth =1,
id ="div_1"});
1. Lösung mit Dynamik
In C # 4.0 und höheren Versionen können Sie einfach in dynamisch umwandeln und schreiben:
if(nodes.Any(n =>((dynamic)n).Checked==false))Console.WriteLine("found not checked element!");
Hinweis: Hierbei wird eine späte Bindung verwendet. Dies bedeutet, dass nur zur Laufzeit erkannt wird, wenn das Objekt keine CheckedEigenschaft hat, und RuntimeBinderExceptionin diesem Fall eine ausgelöst wird. Wenn Sie also versuchen, eine nicht vorhandene Checked2Eigenschaft zu verwenden, wird die folgende Meldung angezeigt Laufzeit :"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'" .
2. Lösung mit Reflexion
Die Lösung mit Reflection funktioniert sowohl mit alten als auch mit neuen C # -Compilerversionen . Bei alten C # -Versionen beachten Sie bitte den Hinweis am Ende dieser Antwort.
Hintergrund
Als Ausgangspunkt habe ich hier eine gute Antwort gefunden . Die Idee ist, den anonymen Datentyp mithilfe von Reflection in ein Wörterbuch zu konvertieren. Das Wörterbuch erleichtert den Zugriff auf die Eigenschaften, da deren Namen als Schlüssel gespeichert sind (Sie können wie darauf zugreifen myDict["myProperty"]).
Angeregt durch den Code in dem obigen Link, habe ich eine Erweiterungsklasse bietet GetProp, UnanonymizePropertiesund UnanonymizeListItemsals Erweiterungsmethoden, die simplify Zugriff auf anonyme Eigenschaften. Mit dieser Klasse können Sie die Abfrage einfach wie folgt ausführen:
if(nodes.UnanonymizeListItems().Any(n =>(bool)n["Checked"]==false)){Console.WriteLine("found not checked element!");}
oder Sie können den Ausdruck nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()als ifBedingung verwenden, die implizit filtert und dann prüft, ob Elemente zurückgegeben werden.
Um das erste Objekt mit der Eigenschaft "Überprüft" abzurufen und seine Eigenschaft "Tiefe" zurückzugeben, können Sie Folgendes verwenden:
var depth = nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
oder kürzer: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Hinweis: Wenn Sie eine Liste von Objekten haben, die nicht unbedingt alle Eigenschaften enthalten müssen (zum Beispiel enthalten einige nicht die Eigenschaft "Geprüft"), und Sie dennoch eine Abfrage basierend auf "Geprüften" Werten erstellen möchten, können Sie dies tun mach das:
if(nodes.UnanonymizeListItems(x =>{var y =((bool?)x.GetProp("Checked",true));return y.HasValue&& y.Value==false;}).Any()){Console.WriteLine("found not checked element!");}
Dies verhindert, dass a KeyNotFoundExceptionauftritt, wenn die Eigenschaft "Checked" nicht vorhanden ist.
Die folgende Klasse enthält die folgenden Erweiterungsmethoden:
UnanonymizeProperties: Wird verwendet, um die in einem Objekt enthaltenen Eigenschaften zu de-anonymisieren . Diese Methode verwendet Reflexion. Es konvertiert das Objekt in ein Wörterbuch, das die Eigenschaften und seine Werte enthält.
UnanonymizeListItems: Wird verwendet, um eine Liste von Objekten in eine Liste von Wörterbüchern zu konvertieren, die die Eigenschaften enthalten. Es kann optional einen Lambda-Ausdruck enthalten, der zuvor gefiltert werden soll.
GetProp: Wird verwendet, um einen einzelnen Wert zurückzugeben, der dem angegebenen Eigenschaftsnamen entspricht. Ermöglicht die Behandlung nicht vorhandener Eigenschaften als Nullwerte (true) und nicht als KeyNotFoundException (false).
Für die obigen Beispiele müssen Sie lediglich die folgende Erweiterungsklasse hinzufügen:
publicstaticclassAnonymousTypeExtensions{// makes properties of object accessible publicstaticIDictionaryUnanonymizeProperties(thisobject obj){Type type = obj?.GetType();var properties = type?.GetProperties()?.Select(n => n.Name)?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj,null));return properties;}// converts object list into list of properties that meet the filterCriteriapublicstaticList<IDictionary>UnanonymizeListItems(thisList<object> objectList,Func<IDictionary<string,object>,bool> filterCriteria=default){var accessibleList =newList<IDictionary>();foreach(object obj in objectList){var props = obj.UnanonymizeProperties();if(filterCriteria ==default|| filterCriteria((IDictionary<string,object>)props)==true){ accessibleList.Add(props);}}return accessibleList;}// returns specific property, i.e. obj.GetProp(propertyName)// requires prior usage of AccessListItems and selection of one element, because// object needs to be a IDictionary<string, object>publicstaticobjectGetProp(thisobject obj,string propertyName,bool treatNotFoundAsNull =false){try{return((System.Collections.Generic.IDictionary<string,object>)obj)?[propertyName];}catch(KeyNotFoundException){if(treatNotFoundAsNull)returndefault(object);elsethrow;}}}
Hinweis: Der obige Code verwendet die seit C # Version 6.0 verfügbaren nullbedingten Operatoren. Wenn Sie mit älteren C # -Compilern (z. B. C # 3.0) arbeiten, ersetzen Sie diese einfach ?.nach .und ?[nach [, z
var depth = nodes.UnanonymizeListItems().FirstOrDefault(n => n.Contains("Checked"))["depth"];
Wenn Sie nicht gezwungen sind, einen älteren C # -Compiler zu verwenden, behalten Sie diesen unverändert bei, da die Verwendung von Nullbedingungen die Nullbehandlung erheblich vereinfacht.
Hinweis: Wie bei der anderen Lösung mit dynamischer Funktion wird auch bei dieser Lösung eine späte Bindung verwendet. In diesem Fall wird jedoch keine Ausnahme angezeigt. Das Element wird einfach nicht gefunden, wenn Sie auf eine nicht vorhandene Eigenschaft verweisen wie Sie die nullbedingten Operatoren behalten .
Für einige Anwendungen kann es nützlich sein, dass auf die Eigenschaft in Lösung 2 über eine Zeichenfolge verwiesen wird und sie daher parametrisiert werden kann.
object v = d.Foo
), währendGetValue(o, null)
sie null ist, wenn sie nicht vorhanden ist.GetProperty
wird zurückkehrennull
undGetValue
werfen, wenn dies bestandennull
wird. Der Gesamteffekt ist also eine Ausnahme. Die C # 4.0-Version bietet eine aussagekräftigere Ausnahme.Sie können die Eigenschaften des anonymen Typs mithilfe von Reflection durchlaufen. Überprüfen Sie, ob eine "Checked" -Eigenschaft vorhanden ist, und ermitteln Sie deren Wert.
Siehe diesen Blog-Beitrag: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx
Also so etwas wie:
quelle
Die akzeptierte Antwort beschreibt korrekt, wie die Liste deklariert werden soll, und wird für die meisten Szenarien dringend empfohlen.
Ich bin jedoch auf ein anderes Szenario gestoßen, das auch die gestellte Frage abdeckt. Was ist, wenn Sie eine vorhandene Objektliste wie
ViewData["htmlAttributes"]
in MVC verwenden müssen ? Wie können Sie auf seine Eigenschaften zugreifen (sie werden normalerweise über erstelltnew { @style="width: 100px", ... }
)?Für dieses etwas andere Szenario möchte ich Ihnen mitteilen, was ich herausgefunden habe. In den folgenden Lösungen gehe ich von folgender Erklärung aus für
nodes
:1. Lösung mit Dynamik
In C # 4.0 und höheren Versionen können Sie einfach in dynamisch umwandeln und schreiben:
Hinweis: Hierbei wird eine späte Bindung verwendet. Dies bedeutet, dass nur zur Laufzeit erkannt wird, wenn das Objekt keine
Checked
Eigenschaft hat, undRuntimeBinderException
in diesem Fall eine ausgelöst wird. Wenn Sie also versuchen, eine nicht vorhandeneChecked2
Eigenschaft zu verwenden, wird die folgende Meldung angezeigt Laufzeit :"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.2. Lösung mit Reflexion
Die Lösung mit Reflection funktioniert sowohl mit alten als auch mit neuen C # -Compilerversionen . Bei alten C # -Versionen beachten Sie bitte den Hinweis am Ende dieser Antwort.
Hintergrund
Als Ausgangspunkt habe ich hier eine gute Antwort gefunden . Die Idee ist, den anonymen Datentyp mithilfe von Reflection in ein Wörterbuch zu konvertieren. Das Wörterbuch erleichtert den Zugriff auf die Eigenschaften, da deren Namen als Schlüssel gespeichert sind (Sie können wie darauf zugreifen
myDict["myProperty"]
).Angeregt durch den Code in dem obigen Link, habe ich eine Erweiterungsklasse bietet
GetProp
,UnanonymizeProperties
undUnanonymizeListItems
als Erweiterungsmethoden, die simplify Zugriff auf anonyme Eigenschaften. Mit dieser Klasse können Sie die Abfrage einfach wie folgt ausführen:oder Sie können den Ausdruck
nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
alsif
Bedingung verwenden, die implizit filtert und dann prüft, ob Elemente zurückgegeben werden.Um das erste Objekt mit der Eigenschaft "Überprüft" abzurufen und seine Eigenschaft "Tiefe" zurückzugeben, können Sie Folgendes verwenden:
oder kürzer:
nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Hinweis: Wenn Sie eine Liste von Objekten haben, die nicht unbedingt alle Eigenschaften enthalten müssen (zum Beispiel enthalten einige nicht die Eigenschaft "Geprüft"), und Sie dennoch eine Abfrage basierend auf "Geprüften" Werten erstellen möchten, können Sie dies tun mach das:
Dies verhindert, dass a
KeyNotFoundException
auftritt, wenn die Eigenschaft "Checked" nicht vorhanden ist.Die folgende Klasse enthält die folgenden Erweiterungsmethoden:
UnanonymizeProperties
: Wird verwendet, um die in einem Objekt enthaltenen Eigenschaften zu de-anonymisieren . Diese Methode verwendet Reflexion. Es konvertiert das Objekt in ein Wörterbuch, das die Eigenschaften und seine Werte enthält.UnanonymizeListItems
: Wird verwendet, um eine Liste von Objekten in eine Liste von Wörterbüchern zu konvertieren, die die Eigenschaften enthalten. Es kann optional einen Lambda-Ausdruck enthalten, der zuvor gefiltert werden soll.GetProp
: Wird verwendet, um einen einzelnen Wert zurückzugeben, der dem angegebenen Eigenschaftsnamen entspricht. Ermöglicht die Behandlung nicht vorhandener Eigenschaften als Nullwerte (true) und nicht als KeyNotFoundException (false).Für die obigen Beispiele müssen Sie lediglich die folgende Erweiterungsklasse hinzufügen:
Hinweis: Der obige Code verwendet die seit C # Version 6.0 verfügbaren nullbedingten Operatoren. Wenn Sie mit älteren C # -Compilern (z. B. C # 3.0) arbeiten, ersetzen Sie diese einfach
?.
nach.
und?[
nach[
, zWenn Sie nicht gezwungen sind, einen älteren C # -Compiler zu verwenden, behalten Sie diesen unverändert bei, da die Verwendung von Nullbedingungen die Nullbehandlung erheblich vereinfacht.
Hinweis: Wie bei der anderen Lösung mit dynamischer Funktion wird auch bei dieser Lösung eine späte Bindung verwendet. In diesem Fall wird jedoch keine Ausnahme angezeigt. Das Element wird einfach nicht gefunden, wenn Sie auf eine nicht vorhandene Eigenschaft verweisen wie Sie die nullbedingten Operatoren behalten .
Für einige Anwendungen kann es nützlich sein, dass auf die Eigenschaft in Lösung 2 über eine Zeichenfolge verwiesen wird und sie daher parametrisiert werden kann.
quelle
Vor kurzem hatte ich das gleiche Problem in .NET 3.5 (keine Dynamik verfügbar). So habe ich gelöst:
Von irgendwo auf Stackoverflow angepasst:
Holen Sie sich nun das Objekt per Cast zurück:
quelle