如何在C#expression式树中设置字段值?

鉴于:

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

如何编译一个lambdaexpression式来将“target”参数中的字段设置为“value”?

.Net 4.0 :现在有了Expression.Assign ,这很容易做到:

 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 :你不能,你将不得不使用System.Reflection.Emit:

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

正如已经讨论的那样,设置一个字段是有问题的。 你可以(在3.5中)使用一个方法,比如一个属性设置器,但是只能间接的。 正如这里所讨论的,这在4.0中变得更容易。 但是,如果你实际上有属性(不是字段),你可以简单地用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); } } 
 private static Action<object, object> CreateSetAccessor(FieldInfo field) { DynamicMethod setMethod = new DynamicMethod(field.Name, typeof(void), new[] { typeof(object), typeof(object) }); ILGenerator generator = setMethod.GetILGenerator(); LocalBuilder local = generator.DeclareLocal(field.DeclaringType); generator.Emit(OpCodes.Ldarg_0); if (field.DeclaringType.IsValueType) { generator.Emit(OpCodes.Unbox_Any, field.DeclaringType); generator.Emit(OpCodes.Stloc_0, local); generator.Emit(OpCodes.Ldloca_S, local); } else { generator.Emit(OpCodes.Castclass, field.DeclaringType); generator.Emit(OpCodes.Stloc_0, local); generator.Emit(OpCodes.Ldloc_0, local); } generator.Emit(OpCodes.Ldarg_1); if (field.FieldType.IsValueType) { generator.Emit(OpCodes.Unbox_Any, field.FieldType); } else { generator.Emit(OpCodes.Castclass, field.FieldType); } generator.Emit(OpCodes.Stfld, field); generator.Emit(OpCodes.Ret); return (Action<object, object>)setMethod.CreateDelegate(typeof(Action<object, object>)); } 

我曾经做过这个课。 也许这有助于:

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

testing:

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

实际上,有一种方法可以在.NET 3.5中使用Expression Trees来设置属性和字段。 对于某些不支持Delegate.CreateDelegate (除了Reflection.Emit )的PCLconfiguration文件,这可能是唯一的select:

  • 对于领域的诀窍是传递领域作为参考
    例如SetField(ref holder.Field, "NewValue");

  • 该属性(正如Marc所指出的)可以通过反映和调用其setter方法来设置。

概念的完整certificate在下面作为NUnittesting夹具提供。

 [TestFixture] public class CanSetPropAndFieldWithExpressionTreeInNet35 { class Holder { public int Field; public string Prop { get; set; } } public static class FieldAndPropSetter { public static T SetField<T, TField>(T holder, ref TField field, TField value) { field = value; return holder; } public static T SetProp<T>(T holder, Action<T> setProp) { setProp(holder); return holder; } } [Test] public void Can_set_field_with_expression_tree_in_Net35() { // Shows how expression could look like: Func<Holder, Holder> setHolderField = h => FieldAndPropSetter.SetField(h, ref h.Field, 111); var holder = new Holder(); holder = setHolderField(holder); Assert.AreEqual(111, holder.Field); var holderType = typeof(Holder); var field = holderType.GetField("Field"); var fieldSetterMethod = typeof(FieldAndPropSetter).GetMethod("SetField") .MakeGenericMethod(holderType, field.FieldType); var holderParamExpr = Expression.Parameter(holderType, "h"); var fieldAccessExpr = Expression.Field(holderParamExpr, field); // Result expression looks like: h => FieldAndPropSetter.SetField(h, ref h.Field, 222) var setHolderFieldExpr = Expression.Lambda<Func<Holder, Holder>>( Expression.Call(fieldSetterMethod, holderParamExpr, fieldAccessExpr, Expression.Constant(222)), holderParamExpr); var setHolderFieldGenerated = setHolderFieldExpr.Compile(); holder = setHolderFieldGenerated(holder); Assert.AreEqual(222, holder.Field); } [Test] public void Can_set_property_with_expression_tree_in_Net35() { // Shows how expression could look like: Func<Holder, Holder> setHolderProp = h => FieldAndPropSetter.SetProp(h, _ => _.Prop = "ABC"); var holder = new Holder(); holder = setHolderProp(holder); Assert.AreEqual("ABC", holder.Prop); var holderType = typeof(Holder); var prop = holderType.GetProperty("Prop"); var propSet = prop.GetSetMethod(); var holderParamExpr = Expression.Parameter(holderType, "h"); var callSetPropExpr = Expression.Call(holderParamExpr, propSet, Expression.Constant("XXX")); var setPropActionExpr = Expression.Lambda(callSetPropExpr, holderParamExpr); var propSetterMethod = typeof(FieldAndPropSetter).GetMethod("SetProp").MakeGenericMethod(holderType); // Result expression looks like: h => FieldAndPropSetter.SetProp(h, _ => _.Prop = "XXX") var setHolderPropExpr = Expression.Lambda<Func<Holder, Holder>>( Expression.Call(propSetterMethod, holderParamExpr, setPropActionExpr), holderParamExpr); var setHolderPropGenerated = setHolderPropExpr.Compile(); holder = setHolderPropGenerated(holder); Assert.AreEqual("XXX", holder.Prop); } } 

为了完整起见,这里是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(); } }