2008-11-26 11 views
37

को देखते हुए:मैं सी # अभिव्यक्ति वृक्ष में फ़ील्ड मान कैसे सेट करूं?

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

मैं कैसे "लक्ष्य" पैरामीटर "मूल्य" के लिए पर क्षेत्र स्थापित करने के लिए एक लैम्ब्डा अभिव्यक्ति संकलन करते हैं?

उत्तर

66

नेट 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"); 

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

ग्रेट प्रतिक्रिया बैरी, आपने मेरे प्रारंभिक प्रश्न का उत्तर दिया। मैं एक और सवाल पोस्ट करने जा रहा हूं जहां मुझे रूपांतरण को कॉल करने के लिए ओप कोड चाहिए .... धन्यवाद! – TheSoftwareJedi

+0

बस जिज्ञासा, इस दृष्टिकोण का अंतर सिर्फ सिस्टम का उपयोग कर रहा है। रिफ्लेक्शन और सदस्य इन्फोस संपत्ति सेट करने के लिए? – chakrit

+2

चक्रित - यह तेज़ है। –

20

फ़ील्ड सेट करना पहले से ही चर्चा की गई है, समस्याग्रस्त है। आप (3.5 में) एक विधि, जैसे कि प्रॉपर्टी-सेटर - लेकिन केवल अप्रत्यक्ष रूप से कर सकते हैं। 4.0 में यह बहुत आसान हो जाता है, जैसा कि here पर चर्चा की गई है। हालांकि, अगर आप वास्तव में गुण (नहीं क्षेत्रों) है, तो आप एक बहुत बस 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

मुझे यह सुनना अच्छा लगेगा कि यह क्यों नीचे आया है ... एक सुंदर सभ्य पक्ष लगता है- मुझे इंगित करें; केवल गुणों पर लागू होता है, लेकिन प्रतिबिंब की आवश्यकता से बचाता है। प्रवेश या अभिव्यक्ति ... –

+0

मार्क, जब तक कि मुझे गलत न हो, मेरा जवाब पिछली रात भी अचयनित हुआ - मैं आज सुबह 3056 से 3041 तक चला गया। यह पिछली बार TheSoftwareJedi के पिछले जवाब पर भी हुआ था। अजीब निष्क्रिय-आक्रामक लगता है। किसी भी मामले में, मुझसे +1। –

+0

@ बेरी - वास्तव में! वास्तव में उत्सुक ... –

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

ऐसा लगता है कि यह काम करने के लिए 'FieldInfo.DeclaringType' सार्वजनिक होना चाहिए। अन्यथा यह .NET 4.0 और 'TypeAccessException' में .NET 4.0/4.5 में' TypeLoadException' के साथ विफल रहता है (हालांकि 'Expression.Assign' 4.0+ में उपलब्ध है, इसलिए 3.5 के लिए कोई समस्या है)। – Loathing

+0

आह, कभी नहीं। बस 'डायनामिक मोड' कन्स्ट्रक्टर में 'प्रतिबंधित स्कीप दृश्यता' सेट करना पड़ा। – Loathing

3

मैं एक बार इस वर्ग बनाया है। शायद यह मदद करता है:

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

परीक्षण:

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

प्रश्न का उत्तर नहीं दे रहा है। इसके बारे में 'FieldInfo' – nawfal

1

बस पूर्णता यहाँ गेटर है:

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

असल में वहाँ में अभिव्यक्ति पेड़ के साथ गुण और फ़ील्ड सेट करने के लिए एक तरीका है। नेट 3.5।

  • क्षेत्र के लिए चाल के रूप में रेफरी पैरामीटर क्षेत्र से गुजर रहा है,
    उदहारण के लिए: यह कुछ पीसीएल प्रोफाइल के लिए एकमात्र विकल्प है कि Delegate.CreateDelegate का समर्थन नहीं करते (Reflection.Emit के अलावा) हो सकता है SetField(ref holder.Field, "NewValue");

  • संपत्ति (जैसा कि पहले से ही मार्क द्वारा इंगित किया गया है) को इसके सेटर विधि को प्रतिबिंबित और कॉल करके सेट किया जा सकता है।

अवधारणा का पूरा सबूत एनयूनीट परीक्षण स्थिरता के रूप में नीचे प्रदान किया गया है।

[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); 
    } 
} 
संबंधित मुद्दे