【问题标题】:Converting 2 argument Lambda expression to 1 argument Lambda expression (specifying one argument)将 2 个参数 Lambda 表达式转换为 1 个参数 Lambda 表达式(指定一个参数)
【发布时间】:2010-08-20 10:27:06
【问题描述】:

我有表情

Expression<Func<Car, Driver, bool>> CanBeDrivenBy = 
    (car, driver) => car.Category == 'B' && driver.Age > 18;

我想买一些司机可以驾驶的汽车

IQueryable<Cars> cars = ...;
Driver driver = ...;
cars.Where(CanBeDrivenBy);   // Fail, expecting Expression<Func<Car, bool>>

所以我需要将Expression&lt;Func&lt;Car, Driver, bool&gt;&gt; 转换为Expression&lt;Func&lt;Car, bool&gt;&gt;(指定驱动程序)

是的,我可以使用

cars.Where(c => c.Category == 'B' && driver.Age > 18);

但我需要可以动态更改表达式的解决方案。我需要通过表达式(使用实体框架)

【问题讨论】:

    标签: c# lambda linq-to-entities


    【解决方案1】:

    您可以重复使用源表达式主体的修改版本

    using System;
    using System.Linq.Expressions;
    
    public class Program
    {
        public static Expression<Func<T1, TResult>> Bind2nd<T1, T2, TResult>(Expression<Func<T1, T2, TResult>> source, T2 argument)
        {
            Expression arg2 = Expression.Constant(argument, typeof (T2));
            Expression newBody = new Rewriter(source.Parameters[1], arg2).Visit(source.Body);
            return Expression.Lambda<Func<T1, TResult>>(newBody, source.Parameters[0]);
        }
    
        public static void Main(string[] args)
        {
            Expression<Func<string, string, int>> f = (a, b) => a.Length + b.Length;
            Console.WriteLine(f); // (a, b) => (a.Length + b.Length)
    
            Console.WriteLine(Bind2nd(f, "1")); // a => (a.Length + "1".Length)
        }
    
        #region Nested type: Rewriter
    
        private class Rewriter : ExpressionVisitor
        {
            private readonly Expression candidate_;
            private readonly Expression replacement_;
    
            public Rewriter(Expression candidate, Expression replacement)
            {
                candidate_ = candidate;
                replacement_ = replacement;
            }
    
            public override Expression Visit(Expression node)
            {
                return node == candidate_ ? replacement_ : base.Visit(node);
            }
        }
    
        #endregion
    }
    

    【讨论】:

    • 您可能想提及您的示例代码是特定于 .NET 4 的。
    • 唯一绑定到 4.0 的部分是 ExpressionVisitor。它的实现非常简单,因此如有必要,可以针对较小的框架版本对其进行重构
    【解决方案2】:

    这个作品

    我编写了这个函数,通过指定第二个参数将参数数量从 2 个减少到 1 个。

    public static Expression<Func<T1, TResult>> Bind2nd<T1, T2, TResult>(Expression<Func<T1, T2, TResult>> source, T2 argument)
    {
        Expression arg2 = Expression.Constant(argument, typeof(T2));
        var arg1 = Expression.Parameter(typeof(T1));
        return Expression.Lambda<Func<T1, TResult>>(Expression.Invoke(source, arg1, arg2), arg1);
    }
    

    用法:

    IQueryable<Car> cars = ...;
    Driver driver = ...;
    cars.Where(Bind2nd(CanBeDrivenBy, driver));
    

    arg1 是调用之间的临时存储。

    有没有系统等效功能?

    【讨论】:

    • 你可能想提一下这叫做柯里化。
    • 哦,哎呀,这也是你的问题 :) 无论如何,我会这样做。您可能会更进一步,只使用 lambda 表达式的主体。
    • 注意:这不能与实体框架一起使用,因为不支持 InvokeExpression。
    猜你喜欢
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多