Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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"?

like image 627
TheSoftwareJedi Avatar asked Nov 26 '08 18:11

TheSoftwareJedi


2 Answers

.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();     } } 
like image 128
Barry Kelly Avatar answered Oct 07 '22 15:10

Barry Kelly


Setting a field is, as already discussed, problematic. You can can (in 3.5) a single method, such as a property-setter - but only indirectly. This gets much easier in 4.0, as discussed here. However, if you actually have properties (not fields), you can do a lot simply with 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);     } } 
like image 31
Marc Gravell Avatar answered Oct 07 '22 17:10

Marc Gravell