2008-11-26 6 views

Respuesta

66

.Net 4.0: ahora que hay Expression.Assign, esto es fácil de hacer:

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: no se puede, que tendrá que utilizar System.Reflection.Emit en su lugar:

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

Gran respuesta barry, usted respondió mi pregunta inicial. Voy a publicar otra pregunta donde necesito códigos operativos para llamar primero a una conversión ... ¡GRACIAS! – TheSoftwareJedi

+0

Simplemente curioso, ¿cuál es la diferencia de este enfoque versus el simple uso de System.Reflection y MemberInfos para establecer la propiedad? – chakrit

+2

chakrit: es más rápido. –

20

Establecer un campo es, como ya se discutió, problemático. Puede (en 3.5) un único método, como un conjunto de propiedades, pero solo indirectamente. Esto se vuelve mucho más fácil en 4.0, como se discute en here. Sin embargo, si usted realmente tiene propiedades (no campos), se puede hacer mucho simplemente con 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); 
    } 
} 
+1

Me encantaría saber por qué se votó negativamente ... parece un lado bastante decente. señalar a mí; solo se aplica a las propiedades, pero evita la necesidad de Reflection.Emit o Expression ... –

+0

Marc, a menos que esté equivocado, mi respuesta no se seleccionó anoche también - Pasé de 3056 a 3041 esta mañana. Esto también sucedió en mi respuesta anterior a TheSoftwareJedi la última vez. Parece extrañamente pasivo-agresivo. En cualquier caso, +1 de mi parte. –

+0

@Barry - de hecho! Realmente curioso ... –

6
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>)); 
    } 
+0

Parece que el 'FieldInfo.DeclaringType' debe ser público para que esto funcione. De lo contrario, falla con una 'TypeLoadException' en .NET 3.5 y' TypeAccessException' en .NET 4.0/4.5 (aunque 'Expression.Assign' está disponible en 4.0+, por lo que es más un problema para 3.5). – Loathing

+0

Ahh, no importa. Solo tuve que configurar 'restrictedSkipVisibility' en el constructor' DynamicMethod'. – Loathing

3

vez que hice esta clase. Tal vez esto sirva:

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

prueba:

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

No respondiendo la pregunta. Se trata de 'FieldInfo' – nawfal

1

simplemente para la corrección aquí es el captador:

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

En realidad no es una manera de establecer las propiedades y campos con árboles de expresión en. NET 3.5. Es puede ser la única opción para algunos perfiles PCL que no soportan Delegate.CreateDelegate (además de la Reflection.Emit):

  • Para el campo el truco está pasando campo como parámetro ref , por ejemplo,
    SetField(ref holder.Field, "NewValue");

  • La propiedad (como ya señaló Marc) se puede establecer al reflejar y llamar a su método setter.

La prueba completa del concepto se proporciona a continuación como accesorio de prueba NUnit.

[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); 
    } 
} 
Cuestiones relacionadas