Pull to refresh

Установка свойства объекта с использованием лямбда-выражений

Reading time2 min
Views4.3K
Если возникает необходимость одинаковой обработки присвоения значения свойству объекта, то можно воспользоваться лямбда выражениями. Такая задача может возникнуть при проверке или обработке значений, логгировании присвоения и т.д.

Простейший метод, который не учитывает присвоение свойствам вложенных объектов, будет такой:

    public static class ObjectHelper
    {
        public static void Set<T, TProp>(this T obj, Expression<Func<T, TProp>> property, TProp value)
        {
            if (obj == null) throw new ArgumentNullException("obj");
            if (property == null) throw new ArgumentNullException("property");

            var memberExpression = (MemberExpression) property.Body;
            var targetPropertyInfo = (PropertyInfo) memberExpression.Member;

            // здесь можно дополнительно обработать obj или value согласно бизнес логике

            targetPropertyInfo.SetValue(obj, value);
        }
    }

Это сработает только для присвоения свойству самого объекта, но не свойству вложенного объекта. То есть:

            myObject.Set(x => x.MyProperty, "bla-bla-bla"); // РАБОТАЕТ
            myObject.Set(x => x.MyProperty.InnerProperty, "bla-bla-bla"); // НЕ РАБОТАЕТ

Не работает, потому что присвоение в данном случае идет не объекту в myObject.MyProperty, а объекту myObject.
(Причем если типы у MyProperty и myObject одинаковые, то не будет выброшено исключение, и в программе будет скрытая ошибка!)

Для того чтобы сработало и для свойств вложенных объектов, нужно пройтись по дереву выражений и получить нужный объект, свойству которого и будет присваиваться значение:

    public static class ObjectHelper
    {
        public static void Set<T, TProp>(this T obj, Expression<Func<T, TProp>> property, TProp value)
        {
            if (obj == null) throw new ArgumentNullException("obj");
            if (property == null) throw new ArgumentNullException("property");

            object target = obj;
            var memberExpression = (MemberExpression) property.Body;
            var targetPropertyInfo = (PropertyInfo) memberExpression.Member;

            if (memberExpression.Expression.NodeType != ExpressionType.Parameter)
            {
                var expressions = new Stack<MemberExpression>();

                while (memberExpression.Expression.NodeType != ExpressionType.Parameter)
                {
                    memberExpression = (MemberExpression)memberExpression.Expression;
                    expressions.Push(memberExpression);
                }

                while (expressions.Count > 0)
                {
                    var expression = expressions.Pop();
                    var propertyInfo = (PropertyInfo)expression.Member;
                    target = propertyInfo.GetValue(target);
                    if (target == null) throw new NullReferenceException(expression.ToString());
                }
            }

            // здесь можно дополнительно обработать obj, target или value согласно бизнес логике

            targetPropertyInfo.SetValue(target, value);
        }
    }

            myObject.Set(x => x.MyProperty, "bla-bla-bla"); // РАБОТАЕТ
            myObject.Set(x => x.MyProperty.InnerProperty, "bla-bla-bla"); // РАБОТАЕТ
Tags:
Hubs:
Total votes 19: ↑7 and ↓12-5
Comments7

Articles