web-dev-qa-db-fra.com

Comment accéder à une propriété de type anonyme en C #?

J'ai ceci:

List<object> nodes = new List<object>(); 

nodes.Add(
new {
    Checked     = false,
    depth       = 1,
    id          = "div_" + d.Id
});

... et je me demande si je peux alors récupérer la propriété "Checked" de l'objet anonyme. Je ne sais pas si c'est même possible. J'ai essayé de faire ceci:

if (nodes.Any(n => n["Checked"] == false)) ... mais cela ne fonctionne pas.

Merci

94
wgpubs

Si vous souhaitez une liste de types anonymes fortement typée, vous devez également définir un type anonyme. Pour ce faire, le moyen le plus simple consiste à projeter une séquence, telle qu’un tableau, dans une liste, par exemple.

var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();

Ensuite, vous pourrez y accéder comme ceci:

nodes.Any(n => n.Checked);

En raison de la manière dont le compilateur fonctionne, les éléments suivants doivent également fonctionner une fois la liste créée, car les types anonymes ont la même structure et sont donc du même type. Je n'ai pas de compilateur à disposition pour vérifier cela.

nodes.Add(new { Checked = false, /* etc */ });
54
Greg Beech

Si vous stockez l'objet sous le type object, vous devez utiliser la réflexion. Cela est vrai de tout type d'objet, anonyme ou non. Sur un objet o, vous pouvez obtenir son type:

Type t = o.GetType();

Ensuite, à partir de là, vous recherchez une propriété:

PropertyInfo p = t.GetProperty("Foo");

Ensuite, à partir de cela, vous pouvez obtenir une valeur:

object v = p.GetValue(o, null);

Cette réponse est attendue depuis longtemps pour une mise à jour de C # 4:

dynamic d = o;
object v = d.Foo;

Et maintenant, une autre alternative en C # 6:

object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);

Notez qu'en utilisant ?. nous faisons en sorte que la v résultante soit null dans trois situations différentes! 

  1. o est null, donc il n'y a pas d'objet du tout
  2. o est non -null mais n'a pas de propriété Foo
  3. o a une propriété Foo mais sa valeur réelle est null.

Cela n’est donc pas équivalent aux exemples précédents, mais cela peut avoir un sens si vous souhaitez traiter les trois cas de la même manière.

225
Daniel Earwicker

Vous pouvez parcourir les propriétés du type anonyme à l'aide de Reflection. voir s'il y a une propriété "Checked" et s'il y en a alors obtenir sa valeur.

Voir cet article de blog: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-type-anonymous-and-net-reflection-hand-in- hand.aspx

Donc, quelque chose comme:

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!");
    }
}
12
glennkentwell

La réponse acceptée décrit correctement la manière dont la liste doit être déclarée et est vivement 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… .. Si vous devez utiliser une liste d'objets existante, telle que ViewData["htmlAttributes"] dans MVC? Comment pouvez-vous accéder à ses propriétés (elles sont généralement créées via 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 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 les versions C # 4.0 et 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: Ceci utilise une liaison tardive, ce qui signifie qu'il reconnaîtra uniquement au moment de l'exécution si l'objet ne possède pas de propriété Checked et lève une RuntimeBinderException dans ce cas - donc si vous essayez d'utiliser une propriété Checked2 non existante 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

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.

Inspiré par le code dans le lien ci-dessus, j'ai créé une classe d'extension, ce qui simplifie l'accès aux propriétés anonymes. Avec cette classe, vous pouvez simplement faire la requête comme suit:

if (nodes.AccessListItems().Any(n => (bool)n["Checked"] == false))
{
    Console.WriteLine("found not checked element!");
}

Tout ce qui est requis est que vous ajoutiez la classe d'extension ci-dessous:

// simplifies access to anonymous properties
public static class AnonymousTypeExtensions
{
    // make properties of object accessible 
    // eg. x.AccessProperties() or x.AccessProperties()["PropName"]
    public static IDictionary AccessProperties(this object o, string propertyName=null)
    {
        Type type = o?.GetType();
        var properties = type?.GetProperties()
        ?.Select(n => n.Name)
        ?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(o, null));
        return properties;
    }

    // returns specific property, i.e. x.AccessProperty(propertyName)
    public static object AccessProperty(this object o, string propertyName)
    {
        return o?.AccessProperties()?[propertyName];
    }

    // converts object list into list of properties
    public static List<IDictionary> AccessListItems(this List<object> objectList)
    {
        var accessibleList = new List<IDictionary>();
        foreach (object obj in objectList)
        {
            accessibleList.Add(obj.AccessProperties());
        }
        return accessibleList;
    }   
}

Le code ci-dessus utilise les opérateurs null-conditionnel , disponibles depuis la version 6.0 de C #. Si vous travaillez avec d'anciens compilateurs, remplacez simplement ?. par . et ?[ par [. Sinon, conservez-le tel quel, car cela facilite beaucoup la gestion des valeurs nulles.

Remarque: Comme pour l'autre solution 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 vous référez à une propriété non existante. . Ce qui pourrait être utile pour certaines applications est que la propriété soit référencée via une chaîne.

0
Matt

Récemment, j'ai eu le même problème dans .NET 3.5 (pas de dynamique disponible). Voici comment j'ai résolu:

// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };

using (frmFind f = new frmFind(args)) 
{
...
...
}

Adapté de quelque part sur stackoverflow:

// Use a custom cast extension
public static T CastTo<T>(this Object x, T targetType)
{
   return (T)x;
}

Maintenant, récupérez l'objet via cast:

public partial class frmFind: Form
{
    public frmFind(object arguments)
    {

        InitializeComponent();

        var args = arguments.CastTo(new { Title = "", Type = typeof(Nullable) });

        this.Text = args.Title;

        ...
    }
    ...
}
0
orfruit