使用ExpressionVisitor合并两个lambda

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

    static void Main(string[] args)
    {
        string a = "ABC";

        Expression> lambda0 = item => item.Length > 2;
        Expression> lambda1 = item => item.Length < 4;
        var a1 = ReBuildExpression(lambda0, lambda1);
        bool k = a1(a);
        Console.WriteLine(k);

    }

    public static Func ReBuildExpression(Expression> lambd0, Expression> 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> lambda = Expression.Lambda>(expression, parameter);//lambda = {item => ((item.Length > 2) AndAlso (item.Length < 4))}
        return lambda.Compile();//从作用域“”引用了“System.String”类型的变量“item”,但该变量未定义
    }
使用ExpressionVisitor合并两个lambda_第1张图片
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> lambda0 = item => item.Length > 2;
        Expression> lambda1 = item => item.Length < 4;
        var a1 = ReBuildExpression2(lambda0, lambda1);
        bool k = a1(a);
        Console.WriteLine(k);

    }

    public static Func ReBuildExpression2(Expression> lambd0, Expression> 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> lambda = Expression.Lambda>(expression, parameter);//lambda = {item => ((item.Length > 2) AndAlso (item.Length < 4))}
        return lambda.Compile();
    }
使用ExpressionVisitor合并两个lambda_第2张图片
image.png

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

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

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

你可能感兴趣的:(使用ExpressionVisitor合并两个lambda)