【问题标题】:get and set object property using Expression trees instead of Reflection使用表达式树而不是反射获取和设置对象属性
【发布时间】:2016-02-25 19:47:10
【问题描述】:

我想动态获取和设置对象属性如下:

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 调用。

【问题讨论】:

    标签: c# reflection expression-trees dynamic-method


    【解决方案1】:

    您真的要使用表达式树吗?对于这个简单的场景,我会尝试通过获取 IL 生成器,使用 Reflection.Emit API 直接编译成 DynamicMethod。但是..对于表达式树,我为你写了一个助手:

     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 为您提供属性名称:)

    【讨论】:

    • 非常完整且解释清楚。
    猜你喜欢
    • 2015-10-13
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多