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

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

Время на прочтение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

Публикации

Истории

Работа

.NET разработчик
50 вакансий

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
19 сентября
CDI Conf 2024
Москва
20 – 22 сентября
BCI Hack Moscow
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
25 сентября
Конференция Yandex Scale 2024
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн