将LambdaExpression转换为字符串,包括值
本文关键字:包括值 字符串 LambdaExpression 转换 | 更新日期: 2023-09-27 18:04:34
我有一个将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函数要做的是解决"值(类)。将表达式的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;
}
我使用这些字符串作为缓存的键。
这在很多情况下都是不正确的,即使它在你的项目中有效。使用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_separator @@"这样的值,一切都会崩溃。这从一开始就不是一个严格的解决方案,因为您选择字符串作为缓存键。
如果您决定选择其他缓存方法,请勾选此项。
要获取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)
以下是我的答案。理想情况下,这将能够处理抛出的任何可能的表达式。现在它很可能还没有,但它处理了我在测试中向它提出的所有简单、常见的问题。
如果你想出一个例子,这不能处理,把它留在评论中,我会尝试修改函数来处理它。
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;
}
非常快速和肮脏的解决方案是传递参数名称和它的值,然后直接替换它。
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;
}