2016-02-25 2 views
1

Я хотел бы динамически получить и установить объекты свойства следующим образом:получить и установить свойство объекта с помощью выражений деревьев вместо отражения

public class Person 
{ 
    public string Name {get; set; } 
} 

public class Testing 
{ 
    public void Run() 
    { 
     var p = new Person(); 

     SetValue(p, "Name", "Henry"); 

     var name = GetValue(p, "Name"); 
    } 
} 

Пожалуйста, я могу получить помощь в создании в GetValue и SetValue методы с использованием динамического метода (или деревья выражений)?

Я собираюсь сохранить скомпилированные выражения в словаре, чтобы ускорить последующие вызовы get/set.

ответ

2

Вы действительно хотите использовать деревья выражений? для этого простого сценария я попытался бы скомпилировать непосредственно в DynamicMethod, используя Reflection.Emit API, получив IL-генератор. Но .. для деревьев выражений, я написал помощник для вас:

public class PropertyManager : DynamicObject 
    { 
     private static Dictionary<Type, Dictionary<string, GetterAndSetter>> _compiledProperties = new Dictionary<Type, Dictionary<string, GetterAndSetter>>(); 

     private static Object _compiledPropertiesLockObject = new object(); 

     private class GetterAndSetter 
     { 
      public Action<object, Object> Setter { get; set; } 

      public Func<Object, Object> Getter { get; set; } 
     } 

     private Object _object; 

     private Type _objectType; 

     private PropertyManager(Object o) 
     { 
      _object = o; 
      _objectType = o.GetType(); 
     } 

     public static dynamic Wrap(Object o) 
     { 
      if (o == null) 
       return null; // null reference will be thrown 

      var type = o.GetType(); 

      EnsurePropertySettersAndGettersForType(type); 

      return new PropertyManager(o) as dynamic; 
     } 

     private static void EnsurePropertySettersAndGettersForType(Type type) 
     { 
      if (false == _compiledProperties.ContainsKey(type)) 
       lock (_compiledPropertiesLockObject) 
        if (false == _compiledProperties.ContainsKey(type)) 
        { 
         Dictionary<string, GetterAndSetter> _getterAndSetters; 
         _compiledProperties[type] = _getterAndSetters = new Dictionary<string, GetterAndSetter>(); 

         var properties = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); 

         foreach (var property in properties) 
         { 
          var getterAndSetter = new GetterAndSetter(); 
          _getterAndSetters[property.Name] = getterAndSetter; 

          // burn getter and setter 

          if (property.CanRead) 
          { 
           // burn getter 

           var param = Expression.Parameter(typeof(object), "param"); 

           Expression propExpression = Expression.Convert(Expression.Property(Expression.Convert(param, type), property), typeof(object)); 

           var lambda = Expression.Lambda(propExpression, new[] { param }); 

           var compiled = lambda.Compile() as Func<object, object>; 
           getterAndSetter.Getter = compiled; 
          } 

          if (property.CanWrite) 
          { 
           var thisParam = Expression.Parameter(typeof(Object), "this"); 
           var theValue = Expression.Parameter(typeof(Object), "value"); 

           var isValueType = property.PropertyType.IsClass == false && property.PropertyType.IsInterface == false; 

           Expression valueExpression; 
           if (isValueType) 
            valueExpression = Expression.Unbox(theValue, property.PropertyType); 
           else 
            valueExpression = Expression.Convert(theValue, property.PropertyType); 

           var thisExpression = Expression.Property (Expression.Convert(thisParam, type), property); 


           Expression body = Expression.Assign(thisExpression, valueExpression); 

           var block = Expression.Block(new[] 
           { 
            body, 
            Expression.Empty() 
           }); 

           var lambda = Expression.Lambda(block, thisParam, theValue); 

           getterAndSetter.Setter = lambda.Compile() as Action<Object, Object>; 
          } 
         } 
        } 
     } 

     public override bool TryGetMember(GetMemberBinder binder, out object result) 
     { 
      var memberName = binder.Name; 
      result = null; 
      Dictionary<string, GetterAndSetter> dict; 
      GetterAndSetter getterAndSetter; 
      if (false == _compiledProperties.TryGetValue(_objectType, out dict) 
       || false == dict.TryGetValue(memberName, out getterAndSetter)) 
      { 
       return false; 
      } 

      if (getterAndSetter.Getter == null) 
       throw new NotSupportedException("The property has no getter!"); 

      result = getterAndSetter.Getter(_object); 
      return true; 
     } 

     public override bool TrySetMember(SetMemberBinder binder, object value) 
     { 
      var memberName = binder.Name; 

      Dictionary<string, GetterAndSetter> dict; 
      GetterAndSetter getterAndSetter; 
      if (false == _compiledProperties.TryGetValue(_objectType, out dict) 
       || false == dict.TryGetValue(memberName, out getterAndSetter)) 
      { 
       return false; 
      } 

      if (getterAndSetter.Setter == null) 
       throw new NotSupportedException("The property has no getter!"); 

      getterAndSetter.Setter(_object, value); 

      return true; 
     } 
    } 

И это, как вы можете использовать его:

Person p = new Person(); 
p.Name = "mama"; 
var wrapped = PropertyManager.Wrap(p); 

var val = wrapped.Name; // here we are using our compiled method ... 

Совершенно очевидно, что вы можете извлечь мою логику компиляции использовать вместо того, чтобы позволить DLR присвоить вам имя свойства :)

+0

Очень полный и хорошо объясненный. – Timo