开发者

Convert Expression<Func<TInterface, bool>> to Expression<Func<TImplementation, bool>>

开发者 https://www.devze.com 2022-12-12 03:46 出处:网络
Another Linq question =) So I have a certain interface that I can code against and an expression that has this signature

Another Linq question =)

So I have a certain interface that I can code against and an expression that has this signature

 Expression<Func<开发者_运维百科;TInterface, bool>>

At some point I will need to use that expression but it needs to look like this

 Expression<Func<TImplementaion, bool>>

I ve tried this

Expression<Func<TImplementation, bool>> expression = x => myExpression.Compile().Invoke(x);

And Although this compiles the expression gets lost in translatiion Any ideas? Thanks


AFAIK The BCL has very limited support for working with Expressions. I'm afraid that you're going to have to rewrite the expression yourself to change the method parameter type.

It's not hard, but not easy either. Basically, you will clone every node of the Expression (it's a tree) but set the root node's data type to your Func<TImplementation, bool>.

I would look for a different design that accomplishes the same goal but doesn't have this casting requirement - plowing through Expressions is not fun.

Update I've implemented a function that does what you want. I call it CastParam:

public static Expression<Func<TOut, bool>> CastParam<TIn, TOut>(this Expression<Func<TIn, bool>> inExpr) {
    if (inExpr.NodeType == ExpressionType.Lambda &&
        inExpr.Parameters.Count > 0) {

        var inP = inExpr.Parameters[0];
        var outP = Expression.Parameter(typeof(TOut), inP.Name);

        var outBody = inExpr.Body.ConvertAll(
            expr => (expr is ParameterExpression) ? outP : expr);                           
        return Expression.Lambda<Func<TOut,bool>>(
            outBody,
            new ParameterExpression[] { outP });
    }
    else {
        throw new NotSupportedException();
    }
}

All it does is rewrite the expression substituting the old ParamaterType with the new type. Here is my little test:

class TInterface { public int IntVal; }
class TImplementation : TInterface { public int ImplVal; }

void Run ()
{
    Expression<Func<TInterface, bool>> intExpr = (i => i.IntVal == 42);
    Expression<Func<TImplementation, bool>> implExpr = intExpr.CastParam<TInterface, TImplementation> ();

    Console.WriteLine ("{0} --> {1}", intExpr, implExpr);

    var c = implExpr.Compile ();

    Console.WriteLine (c.Invoke (new TImplementation { IntVal = 41, ImplVal = 42 }));
    Console.WriteLine (c.Invoke (new TImplementation { IntVal = 42, ImplVal = 41 }));
}

As expected, it prints:

False
True

The code relies on an Expression rewriter that I wrote (rewrites expression trees from the bottom up):

public static Expression Rewrite(this Expression exp, Func<Expression, Expression> c) {
    Expression clone = null;
    switch (exp.NodeType) {
        case ExpressionType.Equal: {
            var x = exp as BinaryExpression;
            clone = Expression.Equal(Rewrite(x.Left,c), Rewrite(x.Right,c), x.IsLiftedToNull, x.Method);
            } break;
        case ExpressionType.MemberAccess: {
            var x = exp as MemberExpression;
            clone = Expression.MakeMemberAccess(Rewrite(x.Expression,c), x.Member);
            } break;
        case ExpressionType.Constant: {
            var x = exp as ConstantExpression;
            clone = Expression.Constant(x.Value);
            } break;
        case ExpressionType.Parameter: {
            var x = exp as ParameterExpression;
            clone = Expression.Parameter(x.Type, x.Name);
            } break;
        default:
            throw new NotImplementedException(exp.NodeType.ToString());
    }
    return c(clone);
}

The rewriter is obviously incomplete and you'll need to finish it off.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号