Search code examples
c#.netexpressionexpression-trees

Replace part of BlockExpression


Here is a BlockExpression

        var compareTo = GetCompareToExpression<TProperty>(expression, parameters);
        var compareToVariable = compareTo.Key;
        var compareToCall = compareTo.Value;
        var zero = Expression.Constant(0, typeof (int));
        LabelTarget ret = Expression.Label(typeof (int));
        var block = Expression.Block(new[] {compareToVariable},
                                     Expression.Assign(compareToVariable, compareToCall),
                                     Expression.IfThen(Expression.NotEqual(compareToVariable, zero),
                                                       Expression.Return(ret, compareToVariable)),
                                     Expression.Label(ret, zero));
        return block;

and its debug view:

.Block(System.Int32 $compareItem1) {
    $compareItem1 = .Call ($x.Item1).CompareTo($y.Item1);
    .If ($compareItem1 != 0) {
        .Return #Label1 { $compareItem1 }
    } .Else {
        .Default(System.Void)
    };
    .Label
        0
    .LabelTarget #Label1:
}

Now I need to replace .Default(System.Void) with some custom logic in another method. What is the easiest way to do it?


Solution

  • Expressions are immutable, like strings. To modify them, you create a copy of them with the changes you need. Normally you use a subclass of ExpressionVisitor to do it, like:

    public class DefaultVoidExpressionReplacer : ExpressionVisitor
    {
        public Expression To;
    
        protected override Expression VisitDefault(DefaultExpression node)
        {
            if (node.Type == typeof(void))
            {
                return this.Visit(To);
            }
            else
            {
                return base.VisitDefault(node);
            }
        }
    }
    

    and you use it like:

    var newExpression = new DefaultVoidExpressionReplacer 
        { To = replaceExpression }.Visit(yourExpression);
    

    You could even decide to work on a "higher" level: the Expression.IfThen level:

    protected override Expression VisitConditional(ConditionalExpression node)
    {
        DefaultExpression de = node.IfFalse as DefaultExpression;
    
        if (de != null && de.Type == typeof(void))
        {
            return base.Visit(Expression.IfThenElse(node.Test, node.IfTrue, To));
        }
    
        return base.VisitConditional(node);
    }