La réponse acceptée décrit correctement la manière dont la liste doit être déclarée et est fortement recommandée pour la plupart des scénarios.
Mais je suis tombé sur un autre scénario, qui couvre également la question posée. Que se passe-t-il si vous devez utiliser une liste d'objets existante, comme ViewData["htmlAttributes"]
en MVC ? Comment pouvez-vous accéder à ses propriétés (elles sont généralement créées par l'intermédiaire de new { @style="width: 100px", ... }
) ?
Pour ce scénario légèrement différent, je souhaite partager avec vous ce que j'ai découvert. Dans les solutions ci-dessous, je suppose que la déclaration suivante est utilisée pour nodes
:
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
Vous avez maintenant une liste d'objets. Comment pouvez-vous accéder aux propriétés des objets, par exemple, retourner une liste de tous les nœuds où la propriété Checked
est fausse ?
1. Solution avec dynamique
En C# 4.0 et plus vous pouvez simplement convertir en dynamique et écrire :
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found a not checked element!");
Remarque : Il s'agit d'utiliser reliure tardive, ce qui signifie qu'il ne reconnaîtra qu'au moment de l'exécution si l'objet n'a pas d'attribut Checked
et lance un RuntimeBinderException
dans ce cas - donc si vous essayez d'utiliser un Checked2
vous obtiendrez le message suivant au moment de l'exécution : "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.
2. Solution avec réflexion
La solution avec réflexion fonctionne avec l'ancien et le nouveau compilateur C# versions. Pour les anciennes versions de C#, veuillez consulter l'astuce à la fin de cette réponse.
Fondo
Pour commencer, j'ai trouvé une bonne réponse aquí . L'idée est de convertir le type de données anonyme en un dictionnaire en utilisant la réflexion. Le dictionnaire facilite l'accès aux propriétés, puisque leurs noms sont stockés en tant que clés (vous pouvez y accéder de la manière suivante myDict["myProperty"]
).
Inspiré par le code du lien ci-dessus, j'ai créé une classe d'extension qui fournit GetProp
, UnanonymizeProperties
y UnanonymizeListItems
en tant que méthodes d'extension, ce qui simplifie l'accès aux propriétés anonymes. Avec cette classe, vous pouvez simplement effectuer la requête comme suit :
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found a not checked element!");
}
ou vous pouvez utiliser l'expression nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
comme if
qui filtre implicitement et vérifie ensuite 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 moins : 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é "Checked") et que vous souhaitez néanmoins élaborer une requête basée sur les valeurs "Checked", vous pouvez procéder de la manière suivante :
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found a not checked element!");
}
Cela permet d'éviter qu'un KeyNotFoundException
se produit 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. Elle convertit l'objet en un dictionnaire contenant les propriétés et leurs valeurs.
-
UnanonymizeListItems
: Est utilisé pour convertir une liste d'objets en une liste de dictionnaires contenant les propriétés. Il peut éventuellement contenir un expression lambda à filtrer au préalable.
-
GetProp
: Est utilisé pour renvoyer une seule valeur correspondant au nom de la propriété donnée. Permet de traiter les propriétés inexistantes comme des valeurs nulles (true) plutôt que comme des 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;
}
}
}
Pista: Le code ci-dessus utilise la fonction null-conditionnel disponibles depuis la version 6.0 de C# - si vous travaillez avec des opérateurs les anciens compilateurs C# (par exemple C# 3.0), remplacer simplement ?.
par .
y ?[
par [
partout (et faire le traitement des nullités traditionnellement en utilisant if
ou attraper des exceptions de type NullReferenceException), par exemple
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
Comme vous pouvez le constater, la gestion des nullités sans les opérateurs conditionnels null serait lourde ici, car partout où vous les supprimez, vous devez ajouter une vérification des nullités - ou utiliser des instructions catch où il n'est pas si facile de trouver la cause première de l'exception, ce qui se traduit par un code beaucoup plus volumineux et difficile à lire.
Si vous êtes no Si vous êtes obligé d'utiliser un ancien compilateur C#, gardez-le tel quel, car l'utilisation de conditionnels de nullité facilite grandement la gestion des nullités.
Remarque : Comme l'autre solution avec la dynamique, cette solution utilise également la liaison tardive, mais dans ce cas, vous n'obtenez pas d'exception - l'élément ne sera tout simplement pas trouvé si vous faites référence à une propriété inexistante, tant que vous conservez l'attribut null-conditionnel des opérateurs.
Ce qui pourrait être utile pour certaines applications, c'est que la propriété est référencée par une chaîne de caractères dans la solution 2, et qu'elle peut donc être paramétrée.