Accéder à l'indexeur à partir de l'arbre d'expression

c# expression-trees

Question

Je travaille sur une fonction de filtrage. Le filtre sera un arbre d'expression créé par un utilisateur. Il y aura environ 30 champs que l'utilisateur peut utiliser pour le filtrage. Je pense que le meilleur moyen est de créer le modèle objet avec indexeur et d'accéder aux valeurs requises par index de type enum

Voir cet exemple:

enum Field
{
    Name,
    Date,
}

class ObjectModel
{
    object this[Field Key]
    {
        get 
        {
            //...
            return xx;
        }
    }
}

Je voudrais demander comment puis-je accéder à un indexeur à partir d'un arbre d'expression.

Réponse acceptée

L'indexeur est une propriété simple, normalement appelée Item . Cela signifie que vous pouvez accéder à l'indexeur comme toute autre propriété en utilisant son nom.

Le nom de la propriété de l'indexeur peut être modifié par l'implémenteur de la classe au moyen de l' attribut IndexerName .

Pour obtenir de manière fiable le nom réel de la propriété d'indexeur, vous devez réfléchir à la classe et obtenir l' attribut DefaultMember .
Plus d'informations peuvent être trouvées ici .


Réponse populaire

Je vais poster un exemple complet sur la façon d'utiliser un indexeur:

ParameterExpression dictExpr = Expression.Parameter(typeof(Dictionary<string, int>));
ParameterExpression keyExpr = Expression.Parameter(typeof(string));
ParameterExpression valueExpr = Expression.Parameter(typeof(int));

// Simple and direct. Should normally be enough
// PropertyInfo indexer = dictExpr.Type.GetProperty("Item");

// Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
PropertyInfo indexer = (from p in dictExpr.Type.GetDefaultMembers().OfType<PropertyInfo>()
                        // This check is probably useless. You can't overload on return value in C#.
                        where p.PropertyType == typeof(int)
                        let q = p.GetIndexParameters()
                        // Here we can search for the exact overload. Length is the number of "parameters" of the indexer, and then we can check for their type.
                        where q.Length == 1 && q[0].ParameterType == typeof(string)
                        select p).Single();

IndexExpression indexExpr = Expression.Property(dictExpr, indexer, keyExpr);

BinaryExpression assign = Expression.Assign(indexExpr, valueExpr);

var lambdaSetter = Expression.Lambda<Action<Dictionary<string, int>, string, int>>(assign, dictExpr, keyExpr, valueExpr);
var lambdaGetter = Expression.Lambda<Func<Dictionary<string, int>, string, int>>(indexExpr, dictExpr, keyExpr);
var setter = lambdaSetter.Compile();
var getter = lambdaGetter.Compile();

var dict = new Dictionary<string, int>();
setter(dict, "MyKey", 2);
var value = getter(dict, "MyKey");

Pour lire à partir de l'indexeur, IndexExpression contient directement la valeur de la propriété indexée. Pour y écrire, nous devons utiliser Expression.Assign . Tout le reste est une Expression assez vanillée. Comme l'a écrit Daniel, l'indexeur est normalement appelé "élément". Notez que Expression.Property a une surcharge qui accepte directement le nom de l'indexeur (donc "Item" ), mais j'ai choisi de le trouver manuellement (afin qu'il puisse être réutilisé). J'ai même donné un exemple d'utilisation de LINQ pour trouver la surcharge exacte d'indexeur souhaitée.

Tout comme une curiosité, si vous regardez sur MSDN par exemple pour le dictionnaire , sous Propriétés vous trouverez l' article




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi