Lambda 表达式树解析
Posted
技术标签:
【中文标题】Lambda 表达式树解析【英文标题】:Lambda Expression Tree Parsing 【发布时间】:2010-09-19 07:11:54 【问题描述】:我正在尝试在项目中使用 Lambda 表达式来映射到第三方查询 API。所以,我正在手动解析表达式树。
如果我传入如下 lambda 表达式:
p => p.Title == "title"
一切正常。
但是,如果我的 lambda 表达式看起来像:
p => p.Title == myaspdropdown.SelectedValue
使用 .NET 调试器,我看不到该功能的实际价值。相反,我看到的是:
p => p.Title = (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue)
什么给了?当我尝试将表达式的右侧作为字符串抓取时,我得到 (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue)
而不是实际值。 如何获得实际值?
【问题讨论】:
我认为问题是(而且我不是表达式专家,所以这不是答案)是表达式不是 lambda;它描述了您希望检索的数据。它没有被执行,它被解释了。 这意味着该值不会在运行时替换您的表达式,而是在编译时永久设置。为了获取值,解释 lambda 的代码必须了解用户控件的概念以及如何提取它 除非解释表达式的代码可以访问您的控件并且被编码为执行此操作,否则这是不可能的。如果代码有一个重载或另一个执行相同操作但获取实际值的方法,您可以使用它和一个 lambda 在运行时检索该值。 【参考方案1】:请记住,当您将 lambda 表达式作为表达式树处理时,您没有可执行代码。相反,您有一个表达式元素树,构成您编写的表达式。
Charlie Calvert 的a good post 对此进行了详细讨论。包括使用表达式可视化工具调试表达式的示例。
在您的情况下,要获取等式表达式右侧的值,您需要创建一个新的 lambda 表达式,编译它然后调用它。
我已经编写了一个简单的示例 - 希望它能够满足您的需求。
public class Class1
public string Selection get; set;
public void Sample()
Selection = "Example";
Example<Book, bool>(p => p.Title == Selection);
public void Example<T,TResult>(Expression<Func<T,TResult>> exp)
BinaryExpression equality = (BinaryExpression)exp.Body;
Debug.Assert(equality.NodeType == ExpressionType.Equal);
// Note that you need to know the type of the rhs of the equality
var accessorExpression = Expression.Lambda<Func<string>>(equality.Right);
Func<string> accessor = accessorExpression.Compile();
var value = accessor();
Debug.Assert(value == Selection);
public class Book
public string Title get; set;
【讨论】:
【参考方案2】:要获得实际值,您需要将表达式树的逻辑应用于您拥有的任何上下文。
表达式树的全部意义在于它们将逻辑表示为数据,而不是评估表达式。您需要弄清楚 lambda 表达式的真正含义。这可能意味着根据本地数据评估其中的某些部分 - 您需要自己决定。表达式树非常强大,但是解析和使用它们并不是一件简单的事情。 (问问任何写过 LINQ 提供程序的人...... Frans Bouma 曾多次抱怨过这些困难。)
【讨论】:
嗨,约翰-也许这个问题更清楚:***.com/questions/238765/…【参考方案3】:一直在努力解决完全相同的问题,谢谢 Bevan。在扩展上,以下是可用于提取值的通用模式(在我的查询引擎中使用)。
[TestFixture]
public class TestClass
[Test]
public void TEst()
var user = new User Id = 123;
var idToSearch = user.Id;
var query = Creator.CreateQuery<User>()
.Where(x => x.Id == idToSearch);
public class Query<T>
public Query<T> Where(Expression<Func<T, object>> filter)
var rightValue = GenericHelper.GetVariableValue(((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right.Type, ((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right);
Console.WriteLine(rightValue);
return this;
internal class GenericHelper
internal static object GetVariableValue(Type variableType, Expression expression)
var targetMethodInfo = typeof(InvokeGeneric).GetMethod("GetVariableValue");
var genericTargetCall = targetMethodInfo.MakeGenericMethod(variableType);
return genericTargetCall.Invoke(new InvokeGeneric(), new[] expression );
internal class InvokeGeneric
public T GetVariableValue<T>(Expression expression) where T : class
var accessorExpression = Expression.Lambda<Func<T>>(expression);
var accessor = accessorExpression.Compile();
return accessor();
【讨论】:
与其使用你的反射调用 InvokeGeneric,我会做的是Expression<Func<object>>(Expression.Convert(expression, typeof(object))).Compile()
【参考方案4】:
我不确定我是否理解。你在哪里“看到”那个?那是在设计时还是运行时? Lambda 表达式本质上可以被认为是匿名委托,并且将在延迟执行的情况下进行操作。因此,显然,在执行通过该行之前,您不应该期望看到分配的值。 我不认为这真的是你的意思......如果你澄清一下这个问题也许我可以提供帮助:)
【讨论】:
更新问题。它在调试器中[以及当我尝试抓住表达式的右侧时] @Grank:Lambda 表达式可以转换为表达式树或委托。听起来您正在考虑转换为委托。以上是关于Lambda 表达式树解析的主要内容,如果未能解决你的问题,请参考以下文章