Search code examples
c#lambdaexpression-trees

How do I set a field value in an C# Expression tree?


Given:

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

How do I compile a lambda expression to set the field on the "target" parameter to "value"?


Solution

  • .Net 4.0 : now that there's Expression.Assign, this is easy to do:

    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 : you can't, you'll have to use System.Reflection.Emit instead:

    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();
        }
    }