1

I'm replacing a ParameterExpression with another with the following method:

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;
  }
}

I'm using it like this:

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

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

        Expression.Assign( value, valueGetter ),

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

As you see in order to replace the parameter at the moment I need to declare and assign a new, temporary, ParameterExpression.

I was wondering if there's a way to avoid that work and replace a ParameterExpression directly with the Expression providing the value (valueGetter).

somethig like this to be clear:

 return SomeLambdaExpression.Body.ReplaceParameter( valueGetter, 
     SomeLambdaExpression.Body.Parameters[0].Name);
svick
  • 236,525
  • 50
  • 385
  • 514
Mauro Sampietro
  • 2,739
  • 1
  • 24
  • 50
  • 1
    you should replace parameters by instance, not by name and type, that might result in errors!! Drop the name, and use the parameter from the expression provided. Example: https://stackoverflow.com/questions/29448432/pass-expression-parameter-as-argument-to-another-expression/29471092#29471092 MultiParamReplaceVisitor – MBoros May 26 '17 at 14:54
  • @MBoros completely false! what would you do if you had two params of the same type? you surely need the name to identify which one you wanna replace! – Mauro Sampietro May 26 '17 at 15:41
  • 1
    that is why you replace by INSTANCE (not type). The name has no meaning. In list => list.Where(x => x > 4).Select(x => x + 1) you have 2 DIFFERENT parameters, both of type int, and name x. this is one expression tree. You can manually construct trees where all parameters are called the same, and internal ones can have the same name and type as the external ones. You have to go by INSTANCE. – MBoros May 27 '17 at 11:25
  • Ah, ok i got what you mean. In my case parameter instances are lost along the way of building complex expressions. I don't hold references to any ParameterExpression instance so in order to replace them I perform a replacement on each scope knowing you can't declare a varibale with the same name in the same scope. Take a look at my project on github: UltraMapper. – Mauro Sampietro May 29 '17 at 07:49
  • Well, from my experience that is a very bad practice, and can lead to serious headache. If you have the body of a lambdaexpression, then an inner scope might define a parameter with the same name/type. Then inside the inner scope you will still need instance comparison. E.g Exp> pred = x => x > 3; var ints = new List(); Exp> pred2 = x => ints.AsQueryable(pred); Imagine someone managed to actually quote pred inside pred2 (not just a field access on a closure), which is not so hard. if you only have pred2.Body in hand, how do you know which x is in x>3? – MBoros May 31 '17 at 06:47
  • I'll try to refactor the relevant code in my projects to take care of your considerations. By the way, i think you are overemphasizing the problem. The only think i have to remember is to perform a ReplaceParameter every time i want to combine two LambdaExpressions (which inherently have no param problems). – Mauro Sampietro May 31 '17 at 14:49

1 Answers1

1

In your visitor, instead of return base.VisitParameter( _parameter );, you can do just return _expression;.

svick
  • 236,525
  • 50
  • 385
  • 514