将包含变量的Lambda表达式转换为字符串

时间:2015-09-23 12:28:45

标签: c# lambda

我想转换流动的表达

person.Name = "John";
Expression<Func<Person, bool>> exp = x => x.Name == person.Name && x.Age > 20;

这样的字符串:

(x.Name == "John") AndAlso (x.Age > 20)

我使用exp.ToString();方法但结果是:

(x.Name == value(MyNamespace.MyClass+<>c__DisplayClass0).person.Name) AndAlso (x.Age > 20)

如何正确转换表达式?

2 个答案:

答案 0 :(得分:3)

问题是你的表达式引用了一个闭包中作用域的变量,你需要的是一个常量表达式。

您可以使用ExpressionVisitor重写表达式树,以便消除导致常量的memeber访问:

namespace FixVisitor
{
    class Program
    {
        static void Main(string[] args)
        {
            var person = new Person();
            person.Name = "John";

            Expression<Func<Person, bool>> exp = x => x.Name == person.Name && x.Age > 20;

            var modified = new FixVisitor().Visit(exp);
            Console.WriteLine(modified);
        }
    }

    class FixVisitor : ExpressionVisitor
    {
        bool IsMemeberAccessOfAConstant(Expression exp)
        {
            if (exp.NodeType == ExpressionType.MemberAccess)
            {
                var memberAccess = (MemberExpression) exp;
                if (memberAccess.Expression.NodeType == ExpressionType.Constant)
                    return true;
                return IsMemeberAccessOfAConstant(memberAccess.Expression);
            }

            return false;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (IsMemeberAccessOfAConstant(node) && node.Type == typeof(string))
            {
                var item = Expression.Lambda<Func<string>>(node);
                var value = item.Compile()();
                return Expression.Constant(value, typeof(string));
            }

            return base.VisitMember(node);
        }
    }

    class Person
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }
}

答案 1 :(得分:1)

要做你想做的事,你必须将person.Name视为常量,所以我认为你必须在运行时构建Expression:

var pers = Expression.Parameter(typeof(Person), "x"); //The parameter to the expression(its type and its name)
var propName = Expression.Property(pers, "Name"); // The property "Name" of the parameter(x.Name)
var nameAsConstant = Expression.Constant(person.Name); // The value I will compare to x.Name
var equal = Expression.Equal(propName, nameAsConstant); // The comparison(x.Name == "John")
var propAge = Expression.Property(pers, "Age"); // The property "Age" of the parameter(x.Age)
var ageConstant = Expression.Constant(20); // The value I will compare to x.Age
var greater = Expression.GreaterThan(propAge, ageConstant); // The comparison(x.Age > 20)
var conditions = Expression.AndAlso(equal, greater); // Merging the expression with && [(x.Name == "John") AndAlso (x.Age > 20)]
var lambda = Expression.Lambda<Func<Person, bool>>(conditions, pers); // Build the expression
var lambdaStr = lambda.ToString(); //x => ((x.Name == "John") AndAlso (x.Age > 20))

如果您只需要((x.Name == "John") AndAlso (x.Age > 20)),请执行conditions.ToString();