将LambdaExpression转换为String,包括值

时间:2013-03-18 14:02:35

标签: c# linq

我有一个将LambdaExpression转换为字符串的方法。我使用这些字符串作为缓存的键。

string p = "x";
var a = LambdaToString<MyType>(m => m.P == p);

与此不同:

string p = "y";
var a = LambdaToString<MyType>(m => m.P == p);

然而,无论p的值如何,我的LambdaToString方法的当前状态都产生相同的输出。这是:

(MyType.P == value(ConsoleApplication1.Program+<>c__DisplayClass0).p)

我希望我的LambdaToString函数能够将表达式的“value(class).p”部分解析为实际的文字字符串“x”或“y”(视情况而定)。

这是我的LambdaToString方法的当前状态。我不知道我需要做些什么来修改它以产生我想要的输出:

    public static string LambdaToString<T>(Expression<Func<T, bool>> expression)
    {
        string body = expression.Body.ToString();

        foreach (var parm in expression.Parameters)
        {
            var parmName = parm.Name;
            var parmTypeName = parm.Type.Name;
            body = body.Replace(parmName + ".", parmTypeName + ".");
        }

        return body;
    }

4 个答案:

答案 0 :(得分:3)

  

我使用这些字符串作为缓存的键。

在很多情况下都是不正确的,即使它适用于您的项目。使用Expression.ToString()作为键可以通过以下方式轻松击败:

//counter-example 1
Expression<Func<string, bool>> exp1 = s => s == "a";
Expression<Func<string, bool>> exp2 = ss => ss == "a";
//the two will be considered different in your cache solution
//but they are essentially the same, well that's not the worst, see next

//counter-example 2
Expression<Func<int, bool>> exp3 = i => i > 10;
Expression<Func<long, bool>> exp4 = i => i > 10;
//the two will be considered the same in your cache solution
//of course they are different, probably hences runtime exceptions

以上不是答案。如果您不关心这一点,请继续使用“使用字符串作为键”。

您希望缓存表达式,但要识别那些带有常量的外观相同的表达式。那么为什么不用表达式+常量来构建键呢?在您的示例代码中,键将是:

"m => m.P == p @@SPECIAL_SEPERATOR@@ x"
"m => m.P == p @@SPECIAL_SEPERATOR@@ y"

是的,如果一个常量包含像“@@ SPECIAL_SEPERATOR @@”这样的值,那么一切都会崩溃。这从一开始就不是一个严格的解决方案,因为您选择字符串作为缓存键。

如果您决定选择其他缓存方法,请检查this

答案 1 :(得分:2)

嗯,要获得p值,你可以这样做(可能更简单,更健壮,但是)。

public static string LambdaToString<T>(Expression<Func<T, bool>> expression)
    {
        BinaryExpression binaryExpression = expression.Body as BinaryExpression;
        Expression right = binaryExpression.Right;//right part of the "==" of your predicate
        var objectMember = Expression.Convert(right, typeof(object));//convert to object, as we don't know what's in

        var getterLambda = Expression.Lambda<Func<object>>(objectMember);

        var getter = getterLambda.Compile();



        var valueYouWant = getter();//here's the "x" or "y"
        //...

或更短

Expression right = (expression.Body as BinaryExpression).Right;
var valueYouWant = Expression.Lambda(right).Compile().DynamicInvoke();

<强>注意

当然,这不适合很多场景,它只是了解如何获得价值的基础。如果您的谓词是

,它将无法工作
var x = 1;
var y = 2;
var result = LambdaToString<YourType>(v => v.A== x && v.B == y)

答案 2 :(得分:2)

这是我的答案。理想情况下,这将能够处理任何可能的表达式。现在它很可能没有,但它处理了我在测试中投入的所有简单,常见的事情。

如果你想出一个不处理的例子,把它留在评论中,我会尝试修改它来处理它。

    public static string LambdaToString<T>(Expression<Func<T, bool>> expression)
    {

        var replacements = new Dictionary<string, string>();
        WalkExpression(replacements, expression);


        string body = expression.Body.ToString();

        foreach (var parm in expression.Parameters)
        {
            var parmName = parm.Name;
            var parmTypeName = parm.Type.Name;
            body = body.Replace(parmName + ".", parmTypeName + ".");
        }

        foreach (var replacement in replacements)
        {
            body = body.Replace(replacement.Key, replacement.Value);    
        }

        return body;
    }

    private static void WalkExpression(Dictionary<string, string> replacements, Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                string replacementExpression = expression.ToString();
                if (replacementExpression.Contains("value("))
                {
                    string replacementValue = Expression.Lambda(expression).Compile().DynamicInvoke().ToString();
                    if (!replacements.ContainsKey(replacementExpression))
                    {
                        replacements.Add(replacementExpression, replacementValue.ToString());
                    }
                }
                break;

            case ExpressionType.GreaterThan:
            case ExpressionType.GreaterThanOrEqual:
            case ExpressionType.LessThan:
            case ExpressionType.LessThanOrEqual:
            case ExpressionType.OrElse:
            case ExpressionType.AndAlso:
            case ExpressionType.Equal:
                var bexp = expression as BinaryExpression;
                WalkExpression(replacements, bexp.Left);
                WalkExpression(replacements, bexp.Right);
                break;

            case ExpressionType.Call:
                var mcexp = expression as MethodCallExpression;
                foreach (var argument in mcexp.Arguments)
                {
                    WalkExpression(replacements, argument);
                }
                break;

            case ExpressionType.Lambda:
                var lexp = expression as LambdaExpression;
                WalkExpression(replacements, lexp.Body);
                break;

            case ExpressionType.Constant:
                //do nothing
                break;

            default:
                Trace.WriteLine("Unknown type");
                break;
        }

答案 3 :(得分:1)

非常快速和肮脏的解决方案是传递参数名称及其值,然后替换它。

public static string LambdaToString<T>(Expression<Func<T, bool>> expression, string value,string paramName )
        {

            string body = expression.Body.ToString().Replace(paramName,value);


            foreach (var parm in expression.Parameters)
            {
                var parmName = parm.Name;

                var parmTypeName = parm.Type.Name;
                body = body.Replace(parmName + ".", parmTypeName + ".");
            }

            return body;
        }