【问题标题】:Convert Expression<Func<TInterface, bool>> to Expression<Func<TImplementation, bool>>将 Expression<Func<TInterface, bool>> 转换为 Expression<Func<TImplementation, bool>>
【发布时间】:2009-11-19 19:14:43
【问题描述】:

另一个 Linq 问题 =)

所以我有一个可以编码的特定接口和一个具有此签名的表达式

 Expression<Func<TInterface, bool>>

在某些时候我需要使用该表达式,但它需要看起来像这样

 Expression<Func<TImplementaion, bool>>

我试过了

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

虽然这样可以编译,但表达式在翻译中会丢失 有任何想法吗? 谢谢

【问题讨论】:

    标签: linq


    【解决方案1】:

    AFAIK BCL 对使用表达式的支持非常有限。恐怕您将不得不自己重写表达式以更改方法参数类型。

    这并不难,但也不容易。基本上,您将克隆Expression(它是一棵树)的每个节点,但将根节点的数据类型设置为您的Func&lt;TImplementation, bool&gt;

    我会寻找一种不同的设计来实现相同的目标,但没有这种铸造要求 - 浏览表达式并不有趣。

    更新我已经实现了一个功能,可以满足您的需求。我叫它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();
        }
    }
    

    它所做的只是重写表达式,用新类型替换旧的 ParamaterType。这是我的小测试:

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

    正如预期的那样,它会打印:

    假的
    是的

    代码依赖于我编写的Expression 重写器(自下而上重写表达式树):

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

    重写器显然不完整,您需要完成它。

    【讨论】:

    • 感谢您的快速回答,我很害怕 :(。我会再活一段时间,以防有人有其他想法
    • 绝对可以。此外,您最终可以对其设置赏金,也许有人会为您编写代码。 :-)
    • 谢谢你,我暂时做了一些稍微不同的事情(我有一个实现接口的私有类,我从设置的属性重新创建它。它只有几行代码,但我不太满意所以我必须在今天之后重新访问(今天的小版本)
    • ConvertAll 不存在,我错过了什么吗?!
    • @Stacker 我假设你已经弄清楚了,因为它已经将近 7 年了,但我很确定 ConvertAll 只是 Rewrite 函数。这至少对我有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多