65 votes

Sélecteur de propriété Expression<Func<T>>. Comment obtenir/établir la valeur de la propriété sélectionnée

J'ai un objet que je veux faire construire de cette manière :

var foo = new FancyObject(customer, c=>c.Email); //customer has Email property

Comment dois-je déclarer le deuxième paramètre ?

A quoi ressemblera le code qui accédera au setter/getter de la propriété sélectionnée ?

Mise à jour. Il y a plusieurs entités dans le modèle qui ont la propriété Email. Donc probablement la signature ressemblera à :

public FancyObject(Entity holder, Expression<Func<T>> selector)

et l'appel au constructeur

var foo = new FancyObject(customer, ()=>customer.Email);

105voto

Marc Gravell Points 482669

Le paramètre serait un Expression<Func<Customer,string>> selector . La lecture peut se faire via la compilation à plat :

 Func<Customer,string> func = selector.Compile();

alors vous pouvez accéder func(customer) . L'assignation est plus délicate ; pour les sélecteurs simples, vous pouvez espérer que vous pouvez simplement décomposer en :

var prop = (PropertyInfo)((MemberExpression)selector.Body).Member;
prop.SetValue(customer, newValue, null);

Mais des expressions plus complexes nécessiteraient soit un parcours manuel de l'arbre, soit certains des types de nœuds d'expression de la version 4.0 :

        Expression<Func<Customer, string>> email
             = cust => cust.Email;

        var newValue = Expression.Parameter(email.Body.Type);
        var assign = Expression.Lambda<Action<Customer, string>>(
            Expression.Assign(email.Body, newValue),
            email.Parameters[0], newValue);

        var getter = email.Compile();
        var setter = assign.Compile();

6voto

Jon Skeet Points 692016

Il semble que le type doive être générique avec deux paramètres de type - la source et le résultat. Par exemple, vous pourriez utiliser :

var foo = new FancyObject<Customer, string>(customer, c => c.Email);

Le premier paramètre serait de type TSource et le second serait Expression<Func<TSource, TResult>> :

public class FancyObject<TSource, TResult>
{
    private readonly TSource value;
    private readonly Expression<Func<TSource, TResult>> projection;

    public FancyObject(TSource value, 
                       Expression<Func<TSource, TResult>> projection)
    {
        this.value = value;
        this.projection = projection;
    }
}

Vous pouvez simplifier l'utilisation d'une méthode statique dans un type non générique :

var foo = FancyObject.Create(customer, c => c.Email);

Cela peut utiliser l'inférence de type pour déterminer les arguments de type.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X