TransWikia.com

Проверка внутри Expression на null

Stack Overflow на русском Asked by iluxa1810 on January 4, 2021

Допустим, я хочу принимать на вход Expression<Func<T, TProp>> expr, где будет указано поле с которым нужно что-то сделать.

Все бы хорошо, но если я получаю свойство, которое находится во внутреннем объекте и оно равно null, то я вылетаю.

Например, x=>x.Prop1.Prop2.Prop3

На вопросики в выражении, компилятор ругается…:

An expression tree lambda may not contain a null propagating operator

Я как-нибудь могу, получив данный Expression динамически его модифицировать?

2 Answers

За проверку на null и вообще за придание смысла вызову свойства в коде отвечает провайдер. Если ваш окончательный провайдер - это обычный LINQ to Objects, т.е. если вы просто компилируете это выражение в Func<SomeB, int?> - напишите просто Func<SomeB, int?>, выражения вам не нужны.

LINQ to Objects все равно, отдаете вы ему Expression, или делегат - он все равно Expression в этот делегат скомпилирует, отдать сразу делегат проще и дешевле.

Если у вас нужны именно Expression, т.е. предполагается некая динамическая работа с этим деревом выражений до того, как оно будет скомпилировано LINQ to Objects, то добавьте больше динамики в эту работу, дополнительным визитором. Допишите проверки на null на лету. Примерно вот так:

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;

namespace ConsoleApp4
{
    class SomeA
    {
        public int MyProperty { get; set; }
    }

    class SomeB
    {
        public SomeA SomeA { get; set; }
    }

    class NullPropagationVisitor : ExpressionVisitor
    {
        [return: NotNullIfNotNull("node")]
        public override Expression Visit(Expression node)
        {
            return base.Visit(node);
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Member.MemberType == MemberTypes.Property)
            {
                return WrapInConditional(node);
            }
            return base.VisitMember(node);
        }

        private Expression WrapInConditional(MemberExpression node)
        {
            var targetType = node.Type;
            bool addConvert = false;

            if (targetType.IsValueType)
            {
                targetType = typeof(Nullable<>).MakeGenericType(targetType);
                addConvert = true;
            }

            var nullCheck = Expression.Equal(node.Expression, Expression.Constant(null));
            var isNull = Expression.Constant(null, targetType);
            var visitedNode = base.VisitMember(node);
            Expression isNotNull = addConvert ? Expression.Convert(visitedNode, targetType) : visitedNode;

            return Expression.Condition(nullCheck, isNull, isNotNull);
        }

        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType == ExpressionType.Convert)
            {
                return Visit(node.Operand);
            }
            return base.VisitUnary(node);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<SomeB, int?>> ex = someb => someb.SomeA.MyProperty;

            var result = ((Expression<Func<SomeB, int?>>)new NullPropagationVisitor().Visit(ex)).Compile();

            Console.WriteLine(result(new SomeB { }) == null); // True, null
            Console.WriteLine(result(new SomeB { SomeA = new SomeA { MyProperty = 42 } })); // 42
        }
    }
}

Answered by PashaPash on January 4, 2021

Может такой вариант

(x=>{ try { return x.Prop1.Prop2.Prop3 } catch { return null;}})

элегантнее возможно это

(myLambda)


myLambda = () => ....

Answered by Aziz Umarov on January 4, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP