• 动态创建 Lambda 表达式


    作为 Delegate 的更深度进化,Lambda 让我们的代码显得更加简洁和优雅,但同时也面临一个问题,就是如何依据外部条件动态构建一个 Lambda 表达式。或许你会奇怪这个需求是如何产生的…… 首先,Lambda 在 DLinq 中承担了以往 T-SQL 的部分角色;其次,在数据库设计中,我们往往需要依据外部未知的动态条件组合来查询数据。而问题在于作为一种静态语言,我们显然无法用动态语法或者拼接字符串的方法来创建一个Delegate/Lambda,那么如何达到类似的目的呢?CodeDom?Emit?或许最佳的选择是 System.Linq.Expressions.Expression。

    首先我们看一个简单 Lambda 表达式的构成。
    i => i > 5

    在这个表达式中,"i" 被称为 Parameter,"i > 5" 是 Body。我们可以对 Body 进行更进一步的分解,那么 "i > 5" 分别包含参数(i)、操作符(>)以及一个常数(5)。所有这些通过特定顺序的组合,从而构建一个完整的 Lambda 表达式。

    我们通过一些例子,来学习如何动态构建这些表达式。

    例子1
    var ints = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    //var r = ints.Where(i => i > 5); // 要实现的表达式
    
    // 创建参数 i
    var parameter = Expression.Parameter(typeof(int), "i");
    
    // 创建常量5
    var constant = Expression.Constant(5);
    
    // 创建比较表达式 i > 5
    var bin = Expression.GreaterThan(parameter, constant);
    
    // 获取Lambda表达式
    var lambda = Expression.Lambda<Func<int, bool>>(bin, parameter);
    
    // 通过 Compile 方法获取 Delegate
    var _r = ints.Where(lambda.Compile());


    在代码中设置断点,我们可以看到调试器中显示的表达式信息。

    uploads/200708/20_122914_snap1.gif


    .NET FX 3.5 中为 Lambda 新增了一些委托类型。

    (1) 用于处理无返回数据的 Action。
    public delegate void Action()
    public delegate void Action<T> (T arg)
    public delegate void Action<T1, T2> (T1 arg1, T2 arg2)
    public delegate void Action<T1, T2, T3> (T1 arg1, T2 arg2, T3 arg3)
    public delegate void Action<T1, T2, T3, T4> (T1 arg1, T2 arg2, T3 arg3, T4 arg4)

    (2) 用于处理带返回数据的 Func。
    public delegate TResult Func<TResult> ()
    public delegate TResult Func<T, TResult> (T arg)
    public delegate TResult Func<T1, T2, TResult> (T1 arg1, T2 arg2)
    public delegate TResult Func<T1, T2, T3, TResult> (T1 arg1, T2 arg2, T3 arg3)
    public delegate TResult Func<T1, T2, T3, T4, TResult> (T1 arg1, T2 arg2, T3 arg3, T4 arg4)

    我们还可以进行更复杂的组合。

    例子2
    var ints = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    // var r = ints.Where(i => i > 5 && i <= 7); // 要实现的表达式
    
    // 创建参数 i
    var parameter = Expression.Parameter(typeof(int), "i");
    
    // 创建表达式 i > 5 
    var con1 = Expression.Constant(5);
    var bin1 = Expression.GreaterThan(parameter, con1);
    
    // 创建表达式 i <= 7
    var con2 = Expression.Constant(7);
    var bin2 = Expression.LessThanOrEqual(parameter, con2);
    
    // 组合两个表达式
    var body = Expression.And(bin1, bin2);
    
    // 获取 Lambda 表达式
    var lambda = Expression.Lambda<Func<int, bool>>(body, parameter);
    
    var _r = ints.Where(lambda.Compile());

    在例子2中,我们对复杂的表达式进行了分解,并使用 And 完成多个表达式的组装,由此我们可以创建更加复杂的逻辑组合,比如例子3。

    例子3
    var ints = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    // var r = ints.Where(i => (i > 5 && i <= 7) || (i == 3)); // 要实现的表达式
    
    // 创建参数 i
    var parameter = Expression.Parameter(typeof(int), "i");
    
    // 创建表达式 i > 5
    var con1 = Expression.Constant(5);
    var bin1 = Expression.GreaterThan(parameter, con1);
    
    // 创建表达式 i < 7
    var con2 = Expression.Constant(7);
    var bin2 = Expression.LessThanOrEqual(parameter, con2);
    
    // 创建表达式 i == 3
    var con3 = Expression.Constant(3);
    var bin3 = Expression.Equal(parameter, con3);
    
    // 组合 i > 5 && i <= 7
    var body = Expression.And(bin1, bin2);
    
    // 组合 ( i > 5 && i <= 7) OR (i == 3)
    body = Expression.Or(body, bin3);
    
    var lambda = Expression.Lambda<Func<int, bool>>(body, parameter);
    var _r = ints.Where(lambda.Compile());

    我们继续看几个常见的例子。

    例子4
    var ints = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    //var r = ints.Select(i => i % 2 == 0 ? i : 0); // 要实现的表达式
    
    // 创建参数 i
    var parameter = Expression.Parameter(typeof(int), "i");
    
    // 创建表达式 i % 2
    var con1 = Expression.Constant(2);
    var bin1 = Expression.Modulo(parameter, con1);
    
    // 创建表达式 (i % 2) == 0
    var con2 = Expression.Constant(0);
    var bin2 = Expression.Equal(bin1, con2);
    
    // 创建表达式 IIF(((i % 2) = 0), i, 0)
    var bin3 = Expression.Condition(bin2, parameter, Expression.Constant(0));
    
    var lambda = Expression.Lambda<Func<int, int>>(bin3, parameter);
    var _r = ints.Select(lambda.Compile());

    例子5
    var ints = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    // Array.ForEach<int>(ints, i => Console.WriteLine(i)); // 要实现的表达式
    
    // 创建参数i
    var parameter = Expression.Parameter(typeof(int), "i");
    
    // 获取 Console.WriteLine MethodInfo
    MethodInfo method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) });
    
    // 创建表达式 
    var call = Expression.Call(method, parameter);
    
    var lambda = Expression.Lambda<Action<int>>(call, parameter);
    Array.ForEach<int>(ints, lambda.Compile());

    是该花点时间去好好研究一下 System.Linq.Expressions Namespace 了
  • 相关阅读:
    【leetcode】153. 寻找旋转排序数组中的最小值
    vue下载网络图片
    前端开发项目细节
    如何在手机上预览本地h5页面
    react拖拽添加新组件
    js拖入并复制和拖动改变位置和改变大小
    dva model
    postMessage跨源通信
    react-router
    event.stopPropagation()和event.preventDefault(),return false的区别
  • 原文地址:https://www.cnblogs.com/hyl8218/p/2192087.html
Copyright © 2020-2023  润新知