How to modify type parameter of Expression>?

前端 未结 3 1263
青春惊慌失措
青春惊慌失措 2021-01-04 22:59

I have an instance of the following:

Expression>

I wish to convert it to an instance of the following

3条回答
  •  栀梦
    栀梦 (楼主)
    2021-01-04 23:46

    So the method to actually do the mapping isn't that hard, but sadly there isn't a good way that I can see of generalizing it. Here is a method that takes a Func and maps it to a delegate where the parameter is something more derived than T1:

    public static Expression> Foo(
        Expression> expression)
        where NewParam : OldParam
    {
        var param = Expression.Parameter(typeof(NewParam));
        return Expression.Lambda>(
            expression.Body.Replace(expression.Parameters[0], param)
            , param);
    }
    

    This uses the Replace method to replace all instances of one expression with another. The definition is:

    internal class ReplaceVisitor : ExpressionVisitor
    {
        private readonly Expression from, to;
        public ReplaceVisitor(Expression from, Expression to)
        {
            this.from = from;
            this.to = to;
        }
        public override Expression Visit(Expression node)
        {
            return node == from ? to : base.Visit(node);
        }
    }
    
    public static Expression Replace(this Expression expression,
        Expression searchEx, Expression replaceEx)
    {
        return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
    }
    

    Now we can use this method (which should be given a better name) like so:

    Expression> oldExpression = whatever;
    Expression> newExpression =
        Foo(oldExpression);
    

    And of course since Func is actually covariant with respect to its parameters, we can be sure that any calls to this method generate expressions that won't add runtime failure points.

    You could trivially make versions of this for Func, and so on and so forth up through the 16 different types of Func if you wanted, just creating a parameter expression for each, and replacing all of the old ones with new ones. It'd be tedious, but just following the pattern. Given that there needs to be a generic argument for both the old and new parameter types though, and that there's no way of inferring the arguments, that'd get...messy.

提交回复
热议问题