简体   繁体   English

使用表达式树而不是反射来获取和设置对象属性

[英]get and set object property using Expression trees instead of Reflection

I would like to dynamically get and set an objects properties as follows: 我想动态获取和设置对象属性,如下所示:

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

Please could I get help creating the GetValue and SetValue methods using dynamic method (or expression trees)? 我可以使用动态方法(或表达式树)来帮助创建GetValue和SetValue方法吗?

I am intending to save compiled expressions in a dictionary, to speed up future get/set calls. 我打算将编译后的表达式保存在字典中,以加快将来的get / set调用。

Do you really want to use expression trees? 你真的想用表达树吗? for this simple scenario I would try to compile directly into a DynamicMethod using Reflection.Emit API's by getting an IL Generator. 对于这个简单的场景,我会尝试通过获取IL生成器使用Reflection.Emit API直接编译成DynamicMethod。 But .. for expression trees, i wrote a helper for you: 但是..对于表达树,我为你写了一个帮手:

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

And this is how you can use it: 这就是你如何使用它:

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

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

It is very obvious that you can extract my compilation logic to use strings instead of letting DLR giving the property name for you :) 很明显,您可以提取我的编译逻辑来使用字符串,而不是让DLR为您提供属性名称:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM