【问题标题】:Convert Expression<Func<FromType>> to Expression<Func<ToType>>将表达式<Func<FromType>> 转换为表达式<Func<ToType>>
【发布时间】:2011-08-13 14:24:24
【问题描述】:

如何创建一个通用的辅助方法来将 Func 使用的类型从一种类型转换为表达式中的另一种类型

我有一个Expression&lt;Func&lt;IEmployee, bool&gt;&gt;,我想把它转换成一个

Expression<Func<Employee, bool>>.

第二个类型总是实现第一个类型。我正在努力实现一个通用的解决方案。

编辑

我已将问题编辑得更清楚。

【问题讨论】:

    标签: .net linq expression-trees


    【解决方案1】:

    好吧,您可以创建一个表达式,然后将其参数转换为原始表达式:

    Expression<Func<IEmployee, bool>> source = ...
    
    var param = Expression.Parameter(typeof(Employee));
    
    // Types the argument as the type expected by the source expression
    // and then forwards it...
    var invocationExpr = Expression.Invoke
                         (source, Expression.TypeAs(param, typeof(IEmployee))); 
    
    var result = Expression.Lambda<Func<Employee, bool>>(invocationExpr, param);
    

    如果提供者不支持调用表达式,您可能需要更多 替换源表达式中的参数的复杂解决方案。

    编辑:好的,既然你说你的提供者不喜欢结果表达式,这里有一个替代的例子。这是一个真正粗略的参数替换器应该是什么样子的(我现在只是写这个作为一个示例),但它应该可以很好地满足您的目的。

    public static class ParameterReplacer
    {
        // Produces an expression identical to 'expression'
        // except with 'source' parameter replaced with 'target' parameter.     
        public static Expression<TOutput> Replace<TInput, TOutput>
                     (Expression<TInput> expression,
                      ParameterExpression source,
                      ParameterExpression target)
        {
            return new ParameterReplacerVisitor<TOutput>(source, target)
                      .VisitAndConvert(expression);
        }
    
        private class ParameterReplacerVisitor<TOutput> : ExpressionVisitor
        {
            private ParameterExpression _source;
            private ParameterExpression _target;
    
            public ParameterReplacerVisitor
                  (ParameterExpression source, ParameterExpression target)
            {
                _source = source;
                _target = target;
            }
    
            internal Expression<TOutput> VisitAndConvert<T>(Expression<T> root)
            {
                return (Expression<TOutput>)VisitLambda(root);
            }
    
            protected override Expression VisitLambda<T>(Expression<T> node)
            {
                // Leave all parameters alone except the one we want to replace.
                var parameters = node.Parameters.Select
                                 (p => p == _source ? _target : p);
    
                return Expression.Lambda<TOutput>(Visit(node.Body), parameters);
            }
    
            protected override Expression VisitParameter(ParameterExpression node)
            {
                // Replace the source with the target, visit other params as usual.
                return node == _source ? _target : base.VisitParameter(node);
            }
        }
    }
    

    然后将其用作:

    Expression<Func<IEmployee, bool>> expression = ...
    
    var result = ParameterReplacer.Replace
                    <Func<IEmployee, bool>, Func<Employee, bool>>
                    (expression,
                     expression.Parameters.Single(), 
                     Expression.Parameter(typeof(Employee));
    

    【讨论】:

    • 我正在使用 Teleriks OpenAccess,但它似乎不喜欢生成的表达式。
    • 好的,等一下。将为您提供参数替换器的示例。
    • 不,得到一个空对象异常,只是现在查看它,并将在接下来的 10 分钟内回复。非常感谢您的帮助。
    • Ani,参数替换器似乎工作正常,但无论出于何种原因 Teleriks OpenAccess 都会引发“未设置对象实例的对象引用”异常。真正的痛苦我非常希望我正在做的事情能够正常工作,即将规范传递到存储库以供执行。
    • 祝你好运。如果需要,请发布另一个问题。 :)
    【解决方案2】:

    如果第二种类型总是继承或实现第一种类型,你可以使用

    Func<TOut, bool> Convert<TIn, TOut>(Func<TIn, bool> f) where TOut : TIn
    {
        return (TOut x) => f((TIn)x);
    }
    

    如果没有这样的关系,你不能为此使用泛型。

    【讨论】:

    • 对不起,我应该提到的是,第二种类型总是实现第一种类型。
    • 如果我在表达式中使用 Func 会怎样?
    • 如果你有Expression&lt;Func&lt;IEmployee, bool&gt;&gt;,那么你没有 Func&lt;IEmployee, bool&gt;,你应该在问题中这么说,因为那是一个完全不同的问题。跨度>
    • 抱歉没有完全清楚,问题标题确实提到了转换表达式功能,内容不够清晰。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-25
    相关资源
    最近更新 更多