2008-11-26 28 views

Trả lời

66

Net 4,0: bây giờ mà có Expression.Assign, điều này rất dễ dàng để làm:

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: bạn có thể không, bạn sẽ phải sử dụng System.Reflection.Emit thay vào đó:

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

Rất tiếc, bạn đã trả lời câu hỏi đầu tiên của mình. Tôi sẽ đăng một câu hỏi khác, nơi tôi cần mã op để gọi một chuyển đổi đầu tiên .... THANKS! – TheSoftwareJedi

+0

Chỉ cần tò mò, sự khác biệt của phương pháp này so với chỉ sử dụng System.Reflection và MemberInfos để thiết lập các tài sản là gì? – chakrit

+2

chakrit - nó nhanh hơn. –

20

Đặt trường là, như đã thảo luận, có vấn đề. Bạn có thể (trong 3.5) một phương thức duy nhất, chẳng hạn như một trình thiết lập thuộc tính - nhưng chỉ gián tiếp. Điều này dễ dàng hơn nhiều trong 4.0, như được thảo luận here. Tuy nhiên, nếu bạn thực sự có tài sản (không lĩnh vực), bạn có thể làm được rất nhiều đơn giản với 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

Tôi rất muốn nghe lý do tại sao đã xuống bình chọn ... có vẻ như một bên khá tốt chỉ cho tôi; chỉ áp dụng cho các tài sản, nhưng tránh sự cần thiết cho Reflection.Emit hoặc Expression ... –

+0

Marc, trừ khi tôi nhầm lẫn, tôi đã có câu trả lời của tôi không được chọn đêm qua quá - Tôi đã đi từ 3056 xuống 3041 sáng nay. Điều này cũng đã xảy ra trên câu trả lời trước của tôi cho TheSoftwareJedi lần trước. Có vẻ kỳ quặc thụ động tích cực. Trong mọi trường hợp, +1 từ tôi. –

+0

@Barry - thực sự! Thực sự tò mò ... –

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

Có vẻ như 'FieldInfo.DeclaringType' phải được công khai để làm việc này. Nếu không, nó không thành công với 'TypeLoadException' trong .NET 3.5 và' TypeAccessException' trong .NET 4.0/4.5 (mặc dù 'Expression.Assign' có sẵn trong 4.0+, do đó nhiều vấn đề cho 3.5). – Loathing

+0

Ahh, nevermind. Chỉ cần thiết lập 'restrictedSkipVisibility' trong hàm tạo' DynamicMethod'. – Loathing

3

khi tôi làm lớp này. Có lẽ nó giúp:

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

kiểm tra:

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

Không trả lời câu hỏi. Thông tin về 'FieldInfo' – nawfal

1

Chỉ cần cho đầy đủ ở đây là 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(); 
     } 
    } 
3

Trên thực tế có một cách để thiết lập các thuộc tính và các lĩnh vực với Trees Expression trong. NET 3.5. Đó là có thể là lựa chọn duy nhất đối với một số hồ sơ PCL không hỗ trợ Delegate.CreateDelegate (ngoài Reflection.Emit):

  • Đối với lĩnh vực này lừa được đi qua lĩnh vực như ref tham số,
    ví dụ SetField(ref holder.Field, "NewValue");

  • Thuộc tính (như đã được Marc chỉ ra) có thể được đặt bằng cách phản ánh và gọi phương thức setter của nó.

Bằng chứng đầy đủ về khái niệm được cung cấp dưới đây làm thử nghiệm 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); 
    } 
} 
Các vấn đề liên quan