C# 将LambdaExpression转换为字符串,包括值

C# 将LambdaExpression转换为字符串,包括值,c#,linq,C#,Linq,我有一个将LambdaExpression转换为字符串的方法。我使用这些字符串作为缓存的键 string p = "x"; var a = LambdaToString<MyType>(m => m.P == p); string p=“x”; var a=LambdaToString(m=>m.P==P); 与此不同的是: string p = "y"; var a = LambdaToString<MyType>(m => m.P == p); st

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

string p = "x";
var a = LambdaToString<MyType>(m => m.P == p);
string p=“x”;
var a=LambdaToString(m=>m.P==P);
与此不同的是:

string p = "y";
var a = LambdaToString<MyType>(m => m.P == p);
string p=“y”;
var a=LambdaToString(m=>m.P==P);
但是,我的LambdaToString方法的当前状态是生成相同的输出,而与p的值无关。即:

(MyType.P == value(ConsoleApplication1.Program+<>c__DisplayClass0).p)
(MyType.P==值(控制台应用程序1.Program+c\u显示类0.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;
    }
公共静态字符串LambdaToString(表达式)
{
字符串体=表达式.body.ToString();
foreach(expression.Parameters中的var parm)
{
var parmName=parm.Name;
var parmTypeName=parm.Type.Name;
body=body.Replace(parmName+”,parmTypeName+”);
}
返回体;
}

好吧,要获得p值,您可以这样做(可能更简单、更可靠,但是)

小心

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

var x = 1;
var y = 2;
var result = LambdaToString<YourType>(v => v.A== x && v.B == y)
var x=1;
变量y=2;
变量结果=LambdaToString(v=>v.A==x&&v.B==y)

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

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;
        }
公共静态字符串LambdaToString(表达式、字符串值、字符串参数名)
{
字符串body=expression.body.ToString().Replace(paramName,value);
foreach(expression.Parameters中的var parm)
{
var parmName=parm.Name;
var parmTypeName=parm.Type.Name;
body=body.Replace(parmName+”,parmTypeName+”);
}
返回体;
}

这是我的答案。理想情况下,这将能够处理向其抛出的任何可能的表达式。现在它很可能没有,但它处理了我在测试中抛出的所有简单、常见的东西

如果你提出了一个例子,这个例子不能处理,把它放在注释中,我会尝试修改函数来处理它

    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;
        }
公共静态字符串LambdaToString(表达式)
{
var replacements=newdictionary();
WalkExpression(替换、表达式);
字符串体=表达式.body.ToString();
foreach(expression.Parameters中的var parm)
{
var parmName=parm.Name;
var parmTypeName=parm.Type.Name;
body=body.Replace(parmName+”,parmTypeName+”);
}
foreach(替换中的var替换)
{
body=body.Replace(replacement.Key,replacement.Value);
}
返回体;
}
私有静态表达式(字典替换、表达式)
{
开关(表达式.节点类型)
{
case ExpressionType.MemberAccess:
string replacementExpression=expression.ToString();
if(replacementExpression.Contains(“值(”))
{
string replacementValue=Expression.Lambda(Expression.Compile().DynamicInvoke().ToString();
如果(!replacements.ContainsKey(replacementExpression))
{
Add(replacementExpression,replacementValue.ToString());
}
}
打破
大小写表达式类型。大于:
大小写表达式type.greaterthanor相等:
case ExpressionType.LessThan:
case ExpressionType.lessthanRequire:
case ExpressionType.OrElse:
case ExpressionType.AndAlso:
大小写表达式类型。相等:
var bexp=作为二进制表达式的表达式;
WalkExpression(替换,bexp.Left);
WalkExpression(替换,bexp.Right);
打破
大小写表达式类型。调用:
var mcexp=作为MethodCallExpression的表达式;
foreach(mcexp.Arguments中的var参数)
{
表达式(替换、参数);
}
打破
大小写表达式类型。Lambda:
var lexp=作为LambdaExpression的表达式;
WalkExpression(替换,lexp.Body);
打破
大小写表达式类型。常量:
//无所事事
打破
违约:
Trace.WriteLine(“未知类型”);
打破
}
我使用这些字符串作为缓存的键

string p = "x";
var a = LambdaToString<MyType>(m => m.P == p);
在很多情况下,它都是不正确的,即使它在您的项目中也可以使用。使用
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
是的,如果一个常量包含像“@@SPECIAL_Separator@”这样的值,一切都将崩溃。这从一开始就不是一个严格的解决方案,因为您选择字符串作为缓存键


如果您决定选择另一种缓存方法,请检查。

当然,如果
p
const
变量,这将自动发生(该变量不会被“捕获”并“提升”到生成类上的字段,如
c_udisplayClass0
),但我假设您从某处获得了
p
,因此它不能被声明为
const
(因此我的注释可能没有用)。无法处理具有相同名称但不同类的两个静态方法的情况。例如
()=>Cla
//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"