t3c*_*b0t 20 c# expression-trees expressionvisitor
我知道从MSDN的文章有关如何修改表达式树什么的ExpressionVisitor是应该做的.它应该修改表达式.
然而,他们的例子非常不现实,所以我想知道为什么我需要它?你能说出一些真实世界的案例吗?修改表达式树会有意义吗?或者,为什么必须进行修改?从什么到什么?
它还有许多重载用于访问各种表达式.我如何知道何时应该使用它们以及它们应该返回什么?另一方面,我看到人们使用VisitParameter并返回base.VisitParameter(node)另一方正在返回Expression.Parameter(..).
有一个问题,在数据库中我们有包含0或1(数字)的字段,我们想在应用程序上使用bool.
解决方案是创建一个"Flag"对象,其中包含0或1并且转换为bool.我们在所有应用程序中使用它像bool一样,但是当我们在.Where()子句中使用它时,EntityFramework抱怨它无法调用转换方法.
因此,我们使用表达式访问者在将树发送到EF之前将所有属性访问更改为.Where(x => x.Property)到.Where(x => x.Property.Value == 1).
您能列举一些现实情况下修改表达式树的意义吗?
严格来说,我们绝不会修改表达式树,因为它们是不可变的(至少从外部看,没有保证它不会在内部存储值或具有可变的私有状态)。正是因为它们是不可变的,所以如果我们要创建一个基于我们拥有的,但在某些特定方面有所不同的新的表达式树,那么访问者模式就很有意义。我们必须修改不可变对象的最接近的内容)。
我们可以在Linq本身中找到一些。
在许多方面,最简单的Linq提供程序是linq-to-objects提供程序,它对内存中的可枚举对象起作用。
当它直接将可枚举作为IEnumerable<T>对象接收时,它很简单,因为大多数程序员可以很快地编写大多数方法的未优化版本。例如Where:
foreach (T item in source)
if (pred(item))
yield return item;
Run Code Online (Sandbox Code Playgroud)
等等。但是如何EnumerableQueryable实现IQueryable<T>版本呢?由于可以自动EnumerableQueryable换行,因此IEnumerable<T>我们可以对涉及的一个或多个可枚举对象执行所需的操作,但是我们有一个表达式,该IQueryable<T>表达式根据选择符,谓词等的表达式以及其他表达式来描述,其中我们需要的是对该操作的描述在IEnumerable<T>选择器,谓词等的代表方面
System.Linq.EnumerableRewriterExpressionVisitor完全是这样的重写的实现,然后可以简单地编译和执行结果。
在System.Linq.Expressions其内部,有一些ExpressionVisitor用于不同目的的实现。一个例子是,编译器的解释器形式不能直接处理带引号的表达式中的提升变量,因此它使用访问者将其重写以处理字典中的索引。
除了产生另一个表达式外,an ExpressionVisitor也可以产生另一个结果。再次System.Linq.Expressions有内部示例本身,带有调试字符串,并且ToString()对于许多表达式类型,可以通过访问有问题的表达式来工作。
这可以(尽管不是必须)是数据库查询linq提供程序用来将表达式转换为SQL查询的方法。
我怎么知道什么时候应该使用它们以及它们应该返回什么?
这些方法的默认实现将:
Expression.Constant()),则它将再次返回该节点。Update问题的表达式,将结果传回。Update反过来,它们将返回与新子代类型相同的新节点,或者如果未更改子代,则再次返回同一节点。这样,如果您不知道出于任何目的需要在节点上显式操作,则可能无需更改它。这也意味着这Update是一种获取节点的新版本以进行部分更改的便捷方法。但是,“无论您打算做什么”的含义当然取决于用例。最常见的情况可能是一种极端,另一种极端,要么仅一种或两种表达式类型需要覆盖,要么全部或几乎全部都需要覆盖。
(一个警告是,如果您正在检查具有ReadOnlyCollection诸如的子节点的那些节点的子节点,例如BlockExpression其步骤和变量或其TryExpressioncatch块,那么您有时只会更改那些子节点,如果您没有更改,那么您就是最好自己检查是否存在缺陷(最近已修复,但尚未发布的任何版本),这意味着如果将相同的子代传递Update到原始集合的不同集合中,ReadOnlyCollection则会不必要地创建新的表达式,从而进一步影响通常这是无害的,但会浪费时间和内存。
在ExpressionVisitor使访问者模式进行Expression的。
从概念上讲,问题在于当您导航一Expression棵树时,您只知道任何给定的节点都是Expression,但您不知道具体是什么类型的Expression. 此模式允许您了解Expression您正在使用的类型并为不同类型指定特定于类型的处理。
当您有 时Expression,您只需调用.Modify。在Expression知道自己的类型,所以它会回调适当的override。
public class AndAlsoModifier : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitBinary(BinaryExpression b)
{
if (b.NodeType == ExpressionType.AndAlso)
{
Expression left = this.Visit(b.Left);
Expression right = this.Visit(b.Right);
// Make this binary expression an OrElse operation instead of an AndAlso operation.
return Expression.MakeBinary(ExpressionType.OrElse, left, right, b.IsLiftedToNull, b.Method);
}
return base.VisitBinary(b);
}
}
Run Code Online (Sandbox Code Playgroud)
在这个例子中,如果Expression恰好是 a BinaryExpression,它会回调VisitBinary(BinaryExpression b)例子中给出的。现在,您可以处理它,BinaryExpression知道它是BinaryExpression. 您还可以指定override处理其他类型的其他方法Expression。
值得注意的是,由于这是一个重载解析技巧,visitedExpression会回调最合适的方法。所以,如果有不同种类的BinaryExpression's,那么你可以override为一个特定的子类型编写一个;如果另一个子类型回调,它将只使用默认BinaryExpression处理。
简而言之,这种模式允许您在Expression树中导航,知道Expression您正在使用哪种类型的。
| 归档时间: |
|
| 查看次数: |
5971 次 |
| 最近记录: |