簡體   English   中英

使用表達式樹而不是反射來獲取和設置對象屬性

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

我想動態獲取和設置對象屬性,如下所示:

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

你真的想用表達樹嗎? 對於這個簡單的場景,我會嘗試通過獲取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為您提供屬性名稱:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM