29 votes

Comment définir une valeur de champ dans un arbre d'expression C #?

Donné:

 FieldInfo field = <some valid string field on type T>;
ParameterExpression targetExp = Expression.Parameter(typeof(T), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(string), "value");
 

Comment puis-je compiler une expression lambda pour définir le champ du paramètre "cible" sur "valeur"?

51voto

Barry Kelly Points 30330

.Net 4.0 : maintenant qu'il y a Expression.Assign , c'est facile à faire:

 FieldInfo field = typeof(T).GetField("fieldName");
ParameterExpression targetExp = Expression.Parameter(typeof(T), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(string), "value");

// Expression.Property can be used here as well
MemberExpression fieldExp = Expression.Field(targetExp, field);
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);

var setter = Expression.Lambda<Action<T, string>>
    (assignExp, targetExp, valueExp).Compile();

setter(subject, "new value");
 

.Net 3.5 : vous ne pouvez pas, vous devrez utiliser System.Reflection.Emit à la place:

 class Program
{
    class MyObject
    {
        public int MyField;
    }

    static Action<T,TValue> MakeSetter<T,TValue>(FieldInfo field)
    {
        DynamicMethod m = new DynamicMethod(
            "setter", typeof(void), new Type[] { typeof(T), typeof(TValue) }, typeof(Program));
        ILGenerator cg = m.GetILGenerator();

        // arg0.<field> = arg1
        cg.Emit(OpCodes.Ldarg_0);
        cg.Emit(OpCodes.Ldarg_1);
        cg.Emit(OpCodes.Stfld, field);
        cg.Emit(OpCodes.Ret);

        return (Action<T,TValue>) m.CreateDelegate(typeof(Action<T,TValue>));
    }

    static void Main()
    {
        FieldInfo f = typeof(MyObject).GetField("MyField");

        Action<MyObject,int> setter = MakeSetter<MyObject,int>(f);

        var obj = new MyObject();
        obj.MyField = 10;

        setter(obj, 42);

        Console.WriteLine(obj.MyField);
        Console.ReadLine();
    }
}
 

15voto

Marc Gravell Points 482669

La définition d'un champ est, comme déjà discuté, problématique. Vous pouvez (en 3.5) une seule méthode, comme un set-property - mais seulement indirectement. Cela devient beaucoup plus facile dans 4.0, comme discuté ici . Cependant, si vous avez réellement des propriétés (pas des champs), vous pouvez faire beaucoup de choses simplement avec Delegate.CreateDelegate :

 using System;
using System.Reflection;
public class Foo
{
    public int Bar { get; set; }
}
static class Program
{
    static void Main()
    {
        MethodInfo method = typeof(Foo).GetProperty("Bar").GetSetMethod();
        Action<Foo, int> setter = (Action<Foo, int>)
            Delegate.CreateDelegate(typeof(Action<Foo, int>), method);

        Foo foo = new Foo();
        setter(foo, 12);
        Console.WriteLine(foo.Bar);
    }
}
 

3voto

Manu Points 330

J'ai déjà fait ce cours. Peut-être que cela aide:

 public class GetterSetter<EntityType,propType>
{
    private readonly Func<EntityType, propType> getter;
    private readonly Action<EntityType, propType> setter;
    private readonly string propertyName;
    private readonly Expression<Func<EntityType, propType>> propertyNameExpression;

    public EntityType Entity { get; set; }

    public GetterSetter(EntityType entity, Expression<Func<EntityType, propType>> property_NameExpression)
    {
        Entity = entity;
        propertyName = GetPropertyName(property_NameExpression);
        propertyNameExpression = property_NameExpression;
        //Create Getter
        getter = propertyNameExpression.Compile();
        // Create Setter()
        MethodInfo method = typeof (EntityType).GetProperty(propertyName).GetSetMethod();
        setter = (Action<EntityType, propType>)
                 Delegate.CreateDelegate(typeof(Action<EntityType, propType>), method);
    }


    public propType Value
    {
        get
        {
            return getter(Entity);
        }
        set
        {
            setter(Entity, value);
        }
    }

    protected string GetPropertyName(LambdaExpression _propertyNameExpression)
    {
        var lambda = _propertyNameExpression as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }
        var propertyInfo = memberExpression.Member as PropertyInfo;
        return propertyInfo.Name;
    }
 

tester:

 var gs = new GetterSetter<OnOffElement,bool>(new OnOffElement(), item => item.IsOn);
        gs.Value = true;
        var result = gs.Value;
 

1voto

elios264 Points 62

Juste pour être complet, voici le getter:

     public static IEnumerable<Func<T, object>> GetTypeGetters<T>()
    {
        var fields = typeof (T).GetFields();

        foreach (var field in fields)
        {
            ParameterExpression targetExp = Expression.Parameter(typeof(T), "target");
            UnaryExpression boxedFieldExp = Expression.Convert(Expression.Field(targetExp, field), typeof(object));
            yield return  Expression.Lambda<Func<T,object>>(boxedFieldExp, targetExp).Compile();
        }
    }
 

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