La réponse acceptée décrit correctement comment la liste doit être déclarée et est fortement recommandée pour la plupart des scénarios.
Mais je suis tombé sur un scénario différent, qui couvre également la question posée. Que faire si vous devez utiliser une liste d'objets existante, comme ViewData["htmlAttributes"]
dans MVC ? Comment pouvez - vous accéder à ses propriétés (ils sont généralement créés par l' intermédiaire new { @style="width: 100px", ... }
)?
Pour ce scénario légèrement différent, je veux partager avec vous ce que j'ai découvert. Dans les solutions ci-dessous, je suppose la déclaration suivante pour nodes
:
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
1. Solution avec dynamique
Dans C # 4.0 et les versions supérieures , vous pouvez simplement convertir en dynamique et écrire:
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found not checked element!");
Remarque: cela utilise une liaison tardive, ce qui signifie qu'il ne reconnaîtra qu'au moment de l'exécution si l'objet n'a pas de Checked
propriété et lève un RuntimeBinderException
dans ce cas - donc si vous essayez d'utiliser une Checked2
propriété non existante , vous obtiendrez le message suivant à Durée: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.
2. Solution avec réflexion
La solution avec réflexion fonctionne à la fois avec les anciennes et les nouvelles versions du compilateur C # . Pour les anciennes versions C #, veuillez tenir compte de l'indication à la fin de cette réponse.
Contexte
Comme point de départ, j'ai trouvé une bonne réponse ici . L'idée est de convertir le type de données anonyme en dictionnaire en utilisant la réflexion. Le dictionnaire facilite l'accès aux propriétés, car leurs noms sont stockés sous forme de clés (vous pouvez y accéder comme myDict["myProperty"]
).
Inspiré par le code dans le lien ci-dessus, j'ai créé une classe d'extension fournissant GetProp
, UnanonymizeProperties
et UnanonymizeListItems
comme méthodes d'extension, qui simplifient l'accès aux propriétés anonymes. Avec cette classe, vous pouvez simplement faire la requête comme suit:
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found not checked element!");
}
ou vous pouvez utiliser l'expression nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
comme if
condition, qui filtre implicitement puis vérifie si des éléments sont renvoyés.
Pour obtenir le premier objet contenant la propriété "Checked" et renvoyer sa propriété "depth", vous pouvez utiliser:
var depth = nodes.UnanonymizeListItems()
?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
ou plus court: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Remarque: si vous disposez d'une liste d'objets qui ne contiennent pas nécessairement toutes les propriétés (par exemple, certains ne contiennent pas la propriété "Vérifié") et que vous souhaitez quand même créer une requête basée sur les valeurs "Vérifié", vous pouvez faites ceci:
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found not checked element!");
}
Cela empêche, qu'un KeyNotFoundException
se produise si la propriété "Checked" n'existe pas.
La classe ci-dessous contient les méthodes d'extension suivantes:
UnanonymizeProperties
: Est utilisé pour désanonymiser les propriétés contenues dans un objet. Cette méthode utilise la réflexion. Il convertit l'objet en un dictionnaire contenant les propriétés et ses valeurs.
UnanonymizeListItems
: Permet de convertir une liste d'objets en une liste de dictionnaires contenant les propriétés. Il peut éventuellement contenir une expression lambda à filtrer au préalable.
GetProp
: Est utilisé pour renvoyer une valeur unique correspondant au nom de propriété donné. Permet de traiter les propriétés non existantes comme des valeurs nulles (true) plutôt que comme KeyNotFoundException (false)
Pour les exemples ci-dessus, il suffit d'ajouter la classe d'extension ci-dessous:
public static class AnonymousTypeExtensions
{
// makes properties of object accessible
public static IDictionary UnanonymizeProperties(this object 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 filterCriteria
public static List<IDictionary> UnanonymizeListItems(this List<object> objectList,
Func<IDictionary<string, object>, bool> filterCriteria=default)
{
var accessibleList = new List<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>
public static object GetProp(this object obj, string propertyName,
bool treatNotFoundAsNull = false)
{
try
{
return ((System.Collections.Generic.IDictionary<string, object>)obj)
?[propertyName];
}
catch (KeyNotFoundException)
{
if (treatNotFoundAsNull) return default(object); else throw;
}
}
}
Astuce: Le code ci-dessus utilise les opérateurs conditionnels null , disponibles depuis la version C # 6.0 - si vous travaillez avec des compilateurs C # plus anciens (par exemple C # 3.0), remplacez simplement ?.
par .
et ?[
par [
partout, par exemple
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
Si vous n'êtes pas obligé d'utiliser un ancien compilateur C #, conservez-le tel quel, car l'utilisation de conditions nulles facilite grandement la gestion des valeurs nulles.
Remarque: comme l'autre solution avec dynamique, cette solution utilise également une liaison tardive, mais dans ce cas, vous n'obtenez pas d'exception - elle ne trouvera tout simplement pas l'élément si vous faites référence à une propriété non existante, tant car vous conservez les opérateurs conditionnels nuls .
Ce qui pourrait être utile pour certaines applications, c'est que la propriété est référencée via une chaîne dans la solution 2, elle peut donc être paramétrée.