【问题标题】:Replace parameter value in Expression Tree with a complex Expression用复杂的表达式替换表达式树中的参数值
【发布时间】:2017-05-24 12:08:25
【问题描述】:

我正在使用以下方法将 ParameterExpression 替换为另一个:

public static Expression ReplaceParameter( this Expression expression, 
     ParameterExpression parameter, string name )
{
    return new ExpressionParameterReplacer( parameter, name ).Visit( expression );
}

internal class ExpressionParameterReplacer : ExpressionVisitor
{
    private readonly ParameterExpression _parameter;
    private readonly string _name;

    protected override Expression VisitParameter( ParameterExpression node )
    {
        if( node.Name == _name && (node.Type == _parameter.Type ||
            node.Type.IsAssignableFrom( _parameter.Type )) )
        {
            return base.VisitParameter( _parameter );
        }

        return base.VisitParameter( node );
  }

  internal ExpressionParameterReplacer( ParameterExpression parameter, string name )
  {
       _parameter = parameter;
       _name = name;
  }
}

我是这样使用的:

  ParameterExpression value = Expression.Parameter( ..., "value" );

  return Expression.Block
  (
       new[] { value },

        Expression.Assign( value, valueGetter ),

        SomeLambdaExpression.Body.ReplaceParameter( value, 
            SomeLambdaExpression.Body.Parameters[0].Name);
  )

正如你所见,为了替换参数,我需要声明并分配一个新的临时参数表达式。

我想知道是否有办法避免这项工作并将 ParameterExpression 直接替换为提供值的表达式 (valueGetter)。

这样的事情要清楚:

 return SomeLambdaExpression.Body.ReplaceParameter( valueGetter, 
     SomeLambdaExpression.Body.Parameters[0].Name);

【问题讨论】:

  • 你应该按实例替换参数,而不是按名称和类型,这可能会导致错误!删除名称,并使用提供的表达式中的参数。示例:stackoverflow.com/questions/29448432/…MultiParamReplaceVisitor
  • @MBoros 完全错误!如果你有两个相同类型的参数,你会怎么做?您肯定需要名称来确定要替换哪个!
  • 这就是为什么你用 INSTANCE 代替(不是类型)。这个名字没有任何意义。在 list => list.Where(x => x > 4).Select(x => x + 1) 中,您有 2 个不同的参数,均为 int 类型,名称为 x。这是一棵表达式树。您可以手动构建所有参数都称为相同的树,内部参数可以与外部参数具有相同的名称和类型。你必须通过 INSTANCE 去。
  • 啊,好吧,我明白你的意思了。在我的例子中,参数实例在构建复杂表达式的过程中丢失了。我没有对任何 ParameterExpression 实例的引用,因此为了替换它们,我知道您不能在同一范围内声明具有相同名称的变量,因此我对每个范围执行替换。看看我在 github 上的项目:UltraMapper。
  • 嗯,根据我的经验,这是一种非常糟糕的做法,可能会导致严重的头痛。如果您有 lambda 表达式的主体,则内部范围可能会定义具有相同名称/类型的参数。然后在内部范围内,您仍然需要实例比较。例如 Exp> pred = x => x > 3; var ints = new List(); Exp> pred2 = x => ints.AsQueryable(pred);想象一下有人设法在 pred2 中实际引用了 pred(不仅仅是对闭包的字段访问),这并不难。如果你手头只有 pred2.Body,你怎么知道 x>3 中的哪个 x?

标签: c# expression-trees expressionvisitor


【解决方案1】:

在您的访问者中,您可以只使用return _expression;,而不是return base.VisitParameter( _parameter );

【讨论】:

  • 虽然我建议return base.VisitParameter( _parameter ); 应该是最理想的方式。
猜你喜欢
  • 1970-01-01
  • 2012-06-21
  • 2017-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-15
  • 2011-01-07
相关资源
最近更新 更多