美文网首页
使用ExpressionVisitor合并两个lambda

使用ExpressionVisitor合并两个lambda

作者: 小狼在IT | 来源:发表于2018-06-01 12:50 被阅读0次

    如果我想把两个lambda表达式合并成一个,单纯地是这么想的:

        static void Main(string[] args)
        {
            string a = "ABC";
    
            Expression<Func<string, bool>> lambda0 = item => item.Length > 2;
            Expression<Func<string, bool>> lambda1 = item => item.Length < 4;
            var a1 = ReBuildExpression(lambda0, lambda1);
            bool k = a1(a);
            Console.WriteLine(k);
    
        }
    
        public static Func<string, bool> ReBuildExpression(Expression<Func<string, bool>> lambd0, Expression<Func<string, bool>> lambd1)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(string), "item");//parameter = {item}
            Expression left = lambd0.Body;//lambd0.Body = {(item.Length > 2)}
            Expression right = lambd1.Body;//lambd1.Body = {(item.Length < 4)}
            BinaryExpression expression = Expression.AndAlso(left, right);//expression = {((item.Length > 2) AndAlso (item.Length < 4))}
            Expression<Func<string, bool>> lambda = Expression.Lambda<Func<string, bool>>(expression, parameter);//lambda = {item => ((item.Length > 2) AndAlso (item.Length < 4))}
            return lambda.Compile();//从作用域“”引用了“System.String”类型的变量“item”,但该变量未定义
        }
    
    image.png

    好了,搞不懂这个bug是为啥,以后慢慢研究。

    接着利用ExpressionVisitor来试试
    先建一个继承自ExpressionVisitor的类:

    public class ExpressionVisitorMy : ExpressionVisitor
    {
        private ParameterExpression _Parameter
        {
            get;
            set;
        }
    
        public ExpressionVisitorMy(ParameterExpression ParameterT)
        {
            _Parameter = ParameterT;
        }
    
        public System.Linq.Expressions.Expression Modify(System.Linq.Expressions.Expression exp)
        {
            Expression e = this.Visit(exp);//这个Visit会根据VisitParameter返回的Expression修改这里的exp变量
            return e;
        }
    
        protected override Expression VisitParameter(ParameterExpression p)
        {
            //不管传入的是什么参数,都会返回我的参数
            return _Parameter;
        }
    }
    

    接着利用这个类去合并两个lamdba

        static void Main(string[] args)
        {
            string a = "ABC";
    
            Expression<Func<string, bool>> lambda0 = item => item.Length > 2;
            Expression<Func<string, bool>> lambda1 = item => item.Length < 4;
            var a1 = ReBuildExpression2(lambda0, lambda1);
            bool k = a1(a);
            Console.WriteLine(k);
    
        }
    
        public static Func<string, bool> ReBuildExpression2(Expression<Func<string, bool>> lambd0, Expression<Func<string, bool>> lambd1)
        {
    
            ParameterExpression parameter = Expression.Parameter(typeof(string), "item");//这里第二个参数可以是任意字符值
            ExpressionVisitorMy visitor = new ExpressionVisitorMy(parameter);
            Expression left = visitor.Modify(lambd0.Body);//left = {(item.Length > 2)}
            Expression right = visitor.Modify(lambd1.Body);//right = {(item.Length < 4)}
            BinaryExpression expression = Expression.AndAlso(left, right);//expression = {((item.Length > 2) AndAlso (item.Length < 4))}
            Expression<Func<string, bool>> lambda = Expression.Lambda<Func<string, bool>>(expression, parameter);//lambda = {item => ((item.Length > 2) AndAlso (item.Length < 4))}
            return lambda.Compile();
        }
    
    image.png

    看调试,实际上ReBuildExpression2的每一步返回的值和ReBuildExpression是完全一样的,然而一个报错,一个正确。

    我的山寨理解(不一定对,以后深研后再更正):
    ReBuildExpression中,两个lambda的body里面用的参数虽然叫“item”,但它实际可能运行的时候叫“item123789”,于是跟parameter里面的参数“item”就是不一样的东西了。

    而ReBuildExpression2在visitor.Modify(lambd0.Body)这个步骤,相当于对里面的参数"item"做了一次修改,改成parameter里面的名字。这样就使得传入的参数,与body里面使用的参数名和类型完全一致。

    相关文章

      网友评论

          本文标题:使用ExpressionVisitor合并两个lambda

          本文链接:https://www.haomeiwen.com/subject/xnihsftx.html