Как стать автором
Обновить

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

Время на прочтение2 мин
Количество просмотров4.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"); // РАБОТАЕТ
Теги:
Хабы:
Всего голосов 19: ↑7 и ↓12-5
Комментарии7

Публикации