.NET Framework - 表达式树 (System.Linq.Expressions) (汇总)

创建时间:
2016-04-21 08:50
最近更新:
2018-11-10 01:07

Note: 截至 2017-12-08 "表达式树" 相关均汇总在此 (仅源码另设专文)

所有 示例/测试 代码 均在 TonyLibraryTest\ExpressionTree_Test.cs 中。

Brief

* 若 lambda 表达式被分配给 Expression<TDelegate> 类型的变量,则编译器可以发射代码以创建表示该 lambda 表达式的表达式树。
* C# 编译器只能从 "表达式 Lambda" (或 "单行 Lambda") 生成 表达式树。它无法解析 "语句 lambda" (或 "多行 lambda")。
* 通过 API 创建表达式树需要使用 System.Linq.Expressions.Expression 类。 类包含创建特定类型表达式树节点的静态工厂方法。
* 在 .NET Framework 4 或更高版本中,表达式树 API 还支持赋值表达式和控制流表达式,例如循环、条件块和 try-catch 块等。用 API 能创建 的 表达式树,更复杂于 C# 编译器 基于 lambda 表达式 创建的 表达式树。
* 表达式树应具有永久性。 这意味着如果你想修改某个表达式树,则必须复制该表达式树然后替换其中的节点来创建一个新的表达式树。

-- 表达式树 (C#)

执行表达式树,它可能返回值,或者它可能只是执行操作 (例如 调用方法)。

仅可以执行 表示 lambda 表达式的 表达式树表示 Lambda 表达式的 表达式树 的类型为 LambdaExpressionExpression<TDelegate>。若要执行这些表达式树,请调用 Compile 方法来创建一个可执行的委托,然后调用该委托。

如果委托的类型未知,也就是说 Lambda 表达式的类型为 LambdaExpression,而不是 Expression<TDelegate>,则必须对委托调用 DynamicInvoke 方法,而不是直接调用委托。

如果表达式树不表示 Lambda 表达式,可以通过调用 Lambda<TDelegate>(Expression, IEnumerable<ParameterExpression>) 方法创建一个新的 Lambda 表达式,此表达式的主体为原始表达式树。 然后,按本节前面所述执行此 lambda 表达式。

-- 如何: 执行表达式树 (C#)

表达式树是不可变的,这意味着不能直接对它们进行修改。若要更改表达式树,必须创建现有表达式树的副本,创建此副本后,进行必要的更改。可以使用 ExpressionVisitor 类遍历现有表达式树,以及复制它访问的每个节点。

-- 如何: 修改表达式树 (C#)

* Lambda 表达式不仅可以用来创建委托实例,C# 编译器也能够将他们转换成表达式树。
* Lambda 表达式本身并非委托类型,但它可以隐式或显式转换为一个委托实例。
* 匿名函数这个术语同时涵盖了 匿名方法 和 Lambda 表达式。
* 通过 ILSpy 查看上面的例子,可以发现 Lambda 表达式就是匿名方法,是编译器帮我们进行了转换工作,使我们可以直接使用 Lambda 表达式来进一步简化创建委托实例的代码。
* 表达式树 也称为 表达式目录树,将代码以一种抽象的方式表示成一个对象树,树中每个节点本身都是一个表达式。表达式树不是可执行代码,它是一种数据结构。
* System.Linq.Expressions 命名空间中包含了代表表达式的各个类,所有类都从 Expression 类派生。
* Expression 类包括两个重要属性: NodeType 属性返回所代表的表达式的类型,例如 ConstantAddType 属性代表求值表达式的 .NET 类型,可以把它视为一个返回类型,例如 System.Int32
* BinaryExpression 代表具有两个操作数的任意操作,NodeType 属性能区分由相同的类表示的不同种类的表达式,例如 Add 或者 Subtract
* LambdaExpression 是从 Expression 派生的类型之一。泛型类型 Expression<TDelegate> 又是从 LambdaExpression 派生的。
* ExpressionExpression<TDelegate> 的区别在于,泛型类以静态类型的方式标志了它是什么种类的表达式,也就是说,它确定了返回类型和参数。例如上面的 "Add" 例子,返回值是一个 int 类型、没有参数,所以我们可以使用签名 Func<int> 与之匹配,所以可以用 Expression<Func<int>> 以静态类型的方式来表示该表达式。

-- Lambda 表达式和表达式树

Expression 各个子类的构造函数都是不公开的,要创建表达式树只能使用 Expression 类提供的静态方法。这同时也说明表达式树体系是不能自己扩展的。

表达式树就是一种表示表达式的数据结构。System.Linq.Expression 命名空间下的 Expression 类和它的诸多子类就是这一数据结构的实现。每个表达式都可以表示成 Expression 某个子类的实例。每个 Expression 子类都按照相应表达式的特点储存自己的子节点。

Lambda 表达式最重要的特色是它可以引入一批参数,这批参数可以在函数体表达式中使用。基于这种特色,我们就可以创建出带自定义变量的表达式树,而这些自定义变量就表示成 Lambda 表达式的参数。

-- 博客园 装配脑袋 - Expression Tree 上手指南 (一)

Resource - MSDN

  • LINQ 中的表达式树
  • 表达式树 (C# 和 Visual Basic) - Entrance. 表达式树表示树状数据结构的代码,树状结构中的每个节点都是一个表达式,例如 一个方法调用 或 类似 x < y 的二元运算。可以基于匿名 lambda 表达式让 Visual C# 或 Visual Basic 编译器为您创建表达式树,也可以使用 System.Linq.Expressions 命名空间手动创建表达式树。
  • 表达式树 (C#)
  • System.Linq.Expressions 命名空间 - 该命名空间包含类、接口和枚举可使语言级代码表达式表示为表达式目录树形式的对象。The System.Linq.Expressions namespace contains classes, interfaces and enumerations that enable language-level code expressions to be represented as objects in the form of expression trees.
  • System.Linq.Expressions.Expression 类 - 提供一种基类,表示表达式树节点的类派生自该基类。它还包含用来创建各种节点类型的 static 工厂方法。这是一个 abstract 类。(Tony Remark: 该类中有 309 个 public static 方法。该类 95% 的成员都是 static 方法。)
  • System.Linq.Expressions.Expression<TDelegate> 类 - 将强类型化的 Lambda 表达式表示为表达式树形式的数据结构
  • System.Linq.Expressions.ExpressionType 枚举 - 描述表达式树节点的节点类型。public enum ExpressionType

Resource

博客园 装配脑袋、微软 施凡

  1. Expression Tree 上手指南 (一) - 每个表达式都可以表示成 Expression 的某个子类的实例。Expression 各个子类的构造函数都是不公开的,要创建表达式树只能使用 Expression 类提供的静态方法。
  2. Expression Tree 上手指南 (二) - Linq to SQL 的 QueryProvider 可以将 IQueryable 上的一系列查询动作翻译成 SQL 语句
  3. Expression Tree 上手指南 (三) - 本文演示了 "用 表达式树 实现一个 通用的事件处理程序"

博客园 jesse2013

  1. 由浅入深表达式树 (一) 创建表达式树 - 本文主要是 通过 API 创建表达式树 的示例
  2. 由浅入深表达式树 (二) 遍历表达式树 - 本文的 "表达式的遍历" 小节 介绍了 ExpressionVisitor
  3. 由浅入深表达式树 (完结篇) 重磅打造 Linq To 博客园 - 实现 IQueryable LINQ Provider,详见本文专门小节

博客园 Wolfy

  1. Linq 之 Expression 初见
  2. Linq 之 Expression 进阶
  3. Linq 之 Expression 高级 (常用表达式类型)

赵劼

  1. http://zzk.cnblogs.com/s?w=blog:JeffreyZhao+表达式树
  2. http://www.baidu.com/s?wd=表达式树+site:zhaojie.me
  3. 谈表达式树的缓存 (1): 引言

Contrast

  1. 表达式树 和 ILGenerator Emit 并不存在 所谓的 性能差异

Other

  1. 什么是表达式树,它与表达式、委托有什么区别
  2. 表达式树对性能的影响
  3. 反射 和 表达式 的 性能差异
  4. 轻量级表达式树解析框架 Faller
  5. 解析表达式树
  6. Expression 核心 操作符、表达式、操作方法 - 类似于 本文 "表达式树 的 节点的 节点类型" 小节

Resource - 对象映射

  1. C# 快速高效率复制对象的一种方式 - 表达式树 - 2018-11-06 已整理为 Tl.MapByExpr 类。
  2. 高性能对象映射 (上) - ExpressionMapper
  3. 高性能对象映射 (下) - 表达式树实现
  4. 自己造轮子

Resource - DebugView

Resource - 实现 IQueryable LINQ Provider (IQueryable<T>, IOrderedQueryable<T>, IQueryProvider)

System.Linq.Expressions.Expression<TDelegate>

  • 该类做了一层封装,便于基于 Lambda 表达式 来创建 Lambda 表达式树。它们中间有一个 转换过程,这个 转换过程 发生在 编译时。Lambda 表达式 编译之后是普通的方法,而 表达式树 是以一种树的结构被加载到运行时,可以在运行时去遍历这个树。
  • 通常通过以下代码创建该类的实例: Expression<Func<int>> exprTree = Expression.Lambda<Func<int>>(blockExpr);

构造函数

该类只有一个构造函数,实际上这个构造函数什么也没有做,只是把相关的参数传给了父类 LambdaExpression。源码如下:

namespace System.Linq.Expressions
{
  public sealed class Expression<TDelegate> : LambdaExpression
  {
    internal Expression(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters)
      : base(typeof (TDelegate), name, body, tailCall, parameters)
    {}
  }
}

修改表达式树 (ExpressionVisitor)

  • MSDN: System.Linq.Expressions.ExpressionVisitor Class - Represents a visitor or rewriter for expression trees (表示表达式树的访问者或重写者).
  • ExpressionVisitor 类的所有成员的名称均由 visit 开头。
  • ExpressionVisitor 类共有 26 个 protected internal virtual Expression VisitXxx(XxxExpression node){...}
  • 截至 2018-10-26 理解为 修改表达式树 必须创建 System.Linq.Expressions.ExpressionVisitor 类 的派生类,别无它法。
  • 截至 2018-10-26 理解为 修改表达式树 的设计模式为: 在上述派生类中 根据需要 override (重写) 相应的 VisitXxx()。使用时 只需调用 ExpressionVisitor 类 的 public virtual Expression Visit(Expression node);,它会 "将表达式调度到此类中更专用的访问方法之一",即 上述 "在该派生类中 根据需要 override (重写) 相应的 VisitXxx()"。
  • 如何: 修改表达式树 (C#)》 中有 官方、完整、简单 的示例。
  • 如何: 实现表达式目录树访问器》 可能是 System.Linq.Expressions.ExpressionVisitor 类的某个旧版本的源码,比新版本的源码更好理解。

Resource

表达式树 的 作用/应用场景

  • ORM: 由 表达式树 生成 SQL,最终由 ADO.NET 执行 SQL。
  • 解析字符串 生成 表达式树。
  • 运费系统 中 用户以 字符串形式 输入 计算规则,例如 对应不同的发货渠道,什么重量取哪个区间的费用,多于哪个阶段的费用还要额外费用。解析用户输入的 计算规则 构造 表达式树 从而计算运费。

表达式树 的 节点的 节点类型

以下为 System.Linq.Expressions.ExpressionTypeVisual Studio 2013 - 转到定义 的完整备份:

#region 程序集 System.Core.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\System.Core.dll
#endregion

using System;

namespace System.Linq.Expressions
{
    // 摘要: 
    //     描述表达式目录树的节点的节点类型。
    public enum ExpressionType
    {
        // 摘要: 
        //     加法运算,如 a + b,针对数值操作数,不进行溢出检查。
        Add = 0,
        //
        // 摘要: 
        //     加法运算,如 (a + b),针对数值操作数,进行溢出检查。
        AddChecked = 1,
        //
        // 摘要: 
        //     按位或逻辑 AND 运算,如 C# 中的 (a & b) 和 Visual Basic 中的 (a And b)。
        And = 2,
        //
        // 摘要: 
        //     条件 AND 运算,它仅在第一个操作数的计算结果为 true 时才计算第二个操作数。 它与 C# 中的 (a && b) 和 Visual Basic
        //     中的 (a AndAlso b) 对应。
        AndAlso = 3,
        //
        // 摘要: 
        //     获取一维数组长度的运算,如 array.Length。
        ArrayLength = 4,
        //
        // 摘要: 
        //     一维数组中的索引运算,如 C# 中的 array[index] 或 Visual Basic 中的 array(index)。
        ArrayIndex = 5,
        //
        // 摘要: 
        //     方法调用,如在 obj.sampleMethod() 表达式中。
        Call = 6,
        //
        // 摘要: 
        //     表示 null 合并运算的节点,如 C# 中的 (a ?? b) 或 Visual Basic 中的 If(a, b)。
        Coalesce = 7,
        //
        // 摘要: 
        //     条件运算,如 C# 中的 a > b ? a : b 或 Visual Basic 中的 If(a > b, a, b)。
        Conditional = 8,
        //
        // 摘要: 
        //     一个常量值。
        Constant = 9,
        //
        // 摘要: 
        //     强制转换或转换运算,如 C#中的 (SampleType)obj 或 Visual Basic 中的 CType(obj, SampleType)。
        //     对于数值转换,如果转换后的值对于目标类型来说太大,这不会引发异常。
        Convert = 10,
        //
        // 摘要: 
        //     强制转换或转换运算,如 C#中的 (SampleType)obj 或 Visual Basic 中的 CType(obj, SampleType)。
        //     对于数值转换,如果转换后的值与目标类型大小不符,则引发异常。
        ConvertChecked = 11,
        //
        // 摘要: 
        //     除法运算,如 (a / b),针对数值操作数。
        Divide = 12,
        //
        // 摘要: 
        //     表示相等比较的节点,如 C# 中的 (a == b) 或 Visual Basic 中的 (a = b)。
        Equal = 13,
        //
        // 摘要: 
        //     按位或逻辑 XOR 运算,如 C# 中的 (a ^ b) 或 Visual Basic 中的 (a Xor b)。
        ExclusiveOr = 14,
        //
        // 摘要: 
        //     “大于”比较,如 (a > b)。
        GreaterThan = 15,
        //
        // 摘要: 
        //     “大于或等于”比较,如 (a >= b)。
        GreaterThanOrEqual = 16,
        //
        // 摘要: 
        //     调用委托或 lambda 表达式的运算,如 sampleDelegate.Invoke()。
        Invoke = 17,
        //
        // 摘要: 
        //     lambda 表达式,如 C# 中的 a => a + a 或 Visual Basic 中的 Function(a) a + a。
        Lambda = 18,
        //
        // 摘要: 
        //     按位左移运算,如 (a << b)。
        LeftShift = 19,
        //
        // 摘要: 
        //     “小于”比较,如 (a < b)。
        LessThan = 20,
        //
        // 摘要: 
        //     “小于或等于”比较,如 (a <= b)。
        LessThanOrEqual = 21,
        //
        // 摘要: 
        //     创建新的 System.Collections.IEnumerable 对象并从元素列表中初始化该对象的运算,如 C# 中的 new List<SampleType>(){
        //     a, b, c } 或 Visual Basic 中的 Dim sampleList = { a, b, c }。
        ListInit = 22,
        //
        // 摘要: 
        //     从字段或属性进行读取的运算,如 obj.SampleProperty。
        MemberAccess = 23,
        //
        // 摘要: 
        //     创建新的对象并初始化其一个或多个成员的运算,如 C# 中的 new Point { X = 1, Y = 2 } 或 Visual Basic 中的
        //     New Point With {.X = 1, .Y = 2}。
        MemberInit = 24,
        //
        // 摘要: 
        //     算术余数运算,如 C# 中的 (a % b) 或 Visual Basic 中的 (a Mod b)。
        Modulo = 25,
        //
        // 摘要: 
        //     乘法运算,如 (a * b),针对数值操作数,不进行溢出检查。
        Multiply = 26,
        //
        // 摘要: 
        //     乘法运算,如 (a * b),针对数值操作数,进行溢出检查。
        MultiplyChecked = 27,
        //
        // 摘要: 
        //     算术求反运算,如 (-a)。 不应就地修改 a 对象。
        Negate = 28,
        //
        // 摘要: 
        //     一元加法运算,如 (+a)。 预定义的一元加法运算的结果是操作数的值,但用户定义的实现可以产生特殊结果。
        UnaryPlus = 29,
        //
        // 摘要: 
        //     算术求反运算,如 (-a),进行溢出检查。 不应就地修改 a 对象。
        NegateChecked = 30,
        //
        // 摘要: 
        //     调用构造函数创建新对象的运算,如 new SampleType()。
        New = 31,
        //
        // 摘要: 
        //     创建新的一维数组并从元素列表中初始化该数组的运算,如 C# 中的 new SampleType[]{a, b, c} 或 Visual Basic
        //     中的 New SampleType(){a, b, c}。
        NewArrayInit = 32,
        //
        // 摘要: 
        //     创建新数组(其中每个维度的界限均已指定)的运算,如 C# 中的 new SampleType[dim1, dim2] 或 Visual Basic
        //     中的 New SampleType(dim1, dim2)。
        NewArrayBounds = 33,
        //
        // 摘要: 
        //     按位求补运算或逻辑求反运算。 在 C# 中,它与整型的 (~a) 和布尔值的 (!a) 等效。 在 Visual Basic 中,它与 (Not
        //     a) 等效。 不应就地修改 a 对象。
        Not = 34,
        //
        // 摘要: 
        //     不相等比较,如 C# 中的 (a != b) 或 Visual Basic 中的 (a <> b)。
        NotEqual = 35,
        //
        // 摘要: 
        //     按位或逻辑 OR 运算,如 C# 中的 (a | b) 或 Visual Basic 中的 (a Or b)。
        Or = 36,
        //
        // 摘要: 
        //     短路条件 OR 运算,如 C# 中的 (a || b) 或 Visual Basic 中的 (a OrElse b)。
        OrElse = 37,
        //
        // 摘要: 
        //     对在表达式上下文中定义的参数或变量的引用。 有关详细信息,请参阅 System.Linq.Expressions.ParameterExpression。
        Parameter = 38,
        //
        // 摘要: 
        //     对某个数字进行幂运算的数学运算,如 Visual Basic 中的 (a ^ b)。
        Power = 39,
        //
        // 摘要: 
        //     具有类型为 System.Linq.Expressions.Expression 的常量值的表达式。 System.Linq.Expressions.ExpressionType.Quote
        //     节点可包含对参数的引用,这些参数在该节点表示的表达式的上下文中定义。
        Quote = 40,
        //
        // 摘要: 
        //     按位右移运算,如 (a >> b)。
        RightShift = 41,
        //
        // 摘要: 
        //     减法运算,如 (a - b),针对数值操作数,不进行溢出检查。
        Subtract = 42,
        //
        // 摘要: 
        //     算术减法运算,如 (a - b),针对数值操作数,进行溢出检查。
        SubtractChecked = 43,
        //
        // 摘要: 
        //     显式引用或装箱转换,其中如果转换失败则提供 null,如 C# 中的 (obj as SampleType) 或 Visual Basic 中的
        //     TryCast(obj, SampleType)。
        TypeAs = 44,
        //
        // 摘要: 
        //     类型测试,如 C# 中的 obj is SampleType 或 Visual Basic 中的 TypeOf obj is SampleType。
        TypeIs = 45,
        //
        // 摘要: 
        //     赋值运算,如 (a = b)。
        Assign = 46,
        //
        // 摘要: 
        //     表达式块。
        Block = 47,
        //
        // 摘要: 
        //     调试信息。
        DebugInfo = 48,
        //
        // 摘要: 
        //     一元递减运算,如 C# 和 Visual Basic 中的 (a - 1)。 不应就地修改 a 对象。
        Decrement = 49,
        //
        // 摘要: 
        //     动态操作。
        Dynamic = 50,
        //
        // 摘要: 
        //     默认值。
        Default = 51,
        //
        // 摘要: 
        //     扩展表达式。
        Extension = 52,
        //
        // 摘要: 
        //     “跳转”表达式,如 C# 中的 goto Label 或 Visual Basic 中的 GoTo Label。
        Goto = 53,
        //
        // 摘要: 
        //     一元递增运算,如 C# 和 Visual Basic 中的 (a + 1)。 不应就地修改 a 对象。
        Increment = 54,
        //
        // 摘要: 
        //     索引运算或访问使用参数的属性的运算。
        Index = 55,
        //
        // 摘要: 
        //     标签。
        Label = 56,
        //
        // 摘要: 
        //     运行时变量的列表。 有关详细信息,请参阅 System.Linq.Expressions.RuntimeVariablesExpression。
        RuntimeVariables = 57,
        //
        // 摘要: 
        //     循环,如 for 或 while。
        Loop = 58,
        //
        // 摘要: 
        //     多分支选择运算,如 C# 中的 switch 或 Visual Basic 中的 Select Case。
        Switch = 59,
        //
        // 摘要: 
        //     引发异常的运算,如 throw new Exception()。
        Throw = 60,
        //
        // 摘要: 
        //     try-catch 表达式。
        Try = 61,
        //
        // 摘要: 
        //     取消装箱值类型运算,如 MSIL 中的 unbox 和 unbox.any 指令。
        Unbox = 62,
        //
        // 摘要: 
        //     加法复合赋值运算,如 (a += b),针对数值操作数,不进行溢出检查。
        AddAssign = 63,
        //
        // 摘要: 
        //     按位或逻辑 AND 复合赋值运算,如 C# 中的 (a &= b)。
        AndAssign = 64,
        //
        // 摘要: 
        //     除法复合赋值运算,如 (a /= b),针对数值操作数。
        DivideAssign = 65,
        //
        // 摘要: 
        //     按位或逻辑 XOR 复合赋值运算,如 C# 中的 (a ^= b)。
        ExclusiveOrAssign = 66,
        //
        // 摘要: 
        //     按位左移复合赋值运算,如 (a <<= b)。
        LeftShiftAssign = 67,
        //
        // 摘要: 
        //     算术余数复合赋值运算,如 C# 中的 (a %= b)。
        ModuloAssign = 68,
        //
        // 摘要: 
        //     乘法复合赋值运算,如 (a *= b),针对数值操作数,不进行溢出检查。
        MultiplyAssign = 69,
        //
        // 摘要: 
        //     按位或逻辑 OR 复合赋值运算,如 C# 中的 (a |= b)。
        OrAssign = 70,
        //
        // 摘要: 
        //     对某个数字进行幂运算的复合赋值运算,如 Visual Basic 中的 (a ^= b)。
        PowerAssign = 71,
        //
        // 摘要: 
        //     按位右移复合赋值运算,如 (a >>= b)。
        RightShiftAssign = 72,
        //
        // 摘要: 
        //     减法复合赋值运算,如 (a -= b),针对数值操作数,不进行溢出检查。
        SubtractAssign = 73,
        //
        // 摘要: 
        //     加法复合赋值运算,如 (a += b),针对数值操作数,进行溢出检查。
        AddAssignChecked = 74,
        //
        // 摘要: 
        //     乘法复合赋值运算,如 (a *= b),针对数值操作数,进行溢出检查。
        MultiplyAssignChecked = 75,
        //
        // 摘要: 
        //     减法复合赋值运算,如 (a -= b),针对数值操作数,进行溢出检查。
        SubtractAssignChecked = 76,
        //
        // 摘要: 
        //     一元前缀递增,如 (++a)。 应就地修改 a 对象。
        PreIncrementAssign = 77,
        //
        // 摘要: 
        //     一元前缀递减,如 (--a)。 应就地修改 a 对象。
        PreDecrementAssign = 78,
        //
        // 摘要: 
        //     一元后缀递增,如 (a++)。 应就地修改 a 对象。
        PostIncrementAssign = 79,
        //
        // 摘要: 
        //     一元后缀递减,如 (a--)。 应就地修改 a 对象。
        PostDecrementAssign = 80,
        //
        // 摘要: 
        //     确切类型测试。
        TypeEqual = 81,
        //
        // 摘要: 
        //     二进制反码运算,如 C# 中的 (~a)。
        OnesComplement = 82,
        //
        // 摘要: 
        //     true 条件值。
        IsTrue = 83,
        //
        // 摘要: 
        //     false 条件值。
        IsFalse = 84,
    }
}

Block() 的 返回值

TonyLibraryTest\ExpressionTree_Test.cs 中一系列测试表明: Block() 的最后一个 表达式参数 就是 返回值。但截至 2018-10-24 尚未找到官方文档支持。

P1602 LambdaHelp.cs 备份

trunk\CFTC.HEA\CFTC.Hea.Common\LambdaHelp.cs 的完整备份 (2016-05-23)

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace CFTC.Hea.Common
{
    /// <summary>
    /// 动态生成Lambda表达式树帮助类
    /// </summary>
    public class LambdaHelp
    {
        #region 属性
        /// <summary>
        /// 字段名
        /// </summary>
        public string FieldName { get; set; }
        /// <summary>
        /// 比较关系
        /// </summary>
        public string Compare { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public string Values { get; set; }

        /// <summary>
        /// 条件连接关系
        /// </summary>
        public string JoinType { get; set; }
        #endregion

        #region 方法
        /// <summary>
        /// 动态生成Lambda表达式树
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filter"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> FilterBy<T>(IEnumerable<LambdaHelp> filter)
        {
            string type = string.Empty;
            string fullType = string.Empty;
            string JoinType = string.Empty;
            Expression finalExpr = null;
            ParameterExpression paramExpr = Expression.Parameter(typeof(T), "p");
            foreach(var item in filter) {
                Expression current = null;
                type = typeof(T).GetProperty(item.FieldName).PropertyType.Name.ToLower();
                fullType = typeof(T).GetProperty(item.FieldName).PropertyType.FullName.ToLower();
                #region 根据字段数据类型,生成Lambda表达式
                //如果是字符型,并且是like比较,就调用字符的包含
                switch(type) {
                    case "string":
                        if(item.Compare.ToUpper() == "LIKE")
                            current = GetExpressionString(item.FieldName, item.Values, paramExpr);
                        else
                            current = GetExpression<string>(item.FieldName, item.Values, paramExpr, item.Compare);
                        break;
                    case "int":
                    case "int32":
                        current = GetExpression<int>(item.FieldName, int.Parse(item.Values), paramExpr, item.Compare);
                        break;
                    case "decimal":
                        current = GetExpression<decimal>(item.FieldName, decimal.Parse(item.Values), paramExpr, item.Compare);
                        break;
                    case "double":
                        current = GetExpression<double>(item.FieldName, double.Parse(item.Values), paramExpr, item.Compare);
                        break;
                    case "datetime":
                        current = GetExpression<DateTime>(item.FieldName, DateTime.Parse(item.Values), paramExpr, item.Compare);
                        break;
                    case "nullable`1":
                        if(fullType.IndexOf("int") != -1)
                            current = GetExpression<int?>(item.FieldName, int.Parse(item.Values), paramExpr, item.Compare);
                        break;
                }
                #endregion

                #region 生成Lambda表达式树
                if(current != null) {
                    if(finalExpr == null)
                        finalExpr = current;
                    else if(item.JoinType.ToUpper() == "AND")
                        finalExpr = Expression.And(finalExpr, current);
                    else if(item.JoinType.ToUpper() == "OR")
                        finalExpr = Expression.Or(finalExpr, current);
                }
                JoinType = item.JoinType;
                #endregion
            }
            if(finalExpr == null)
                return p => true;
            else
                return Expression.Lambda<Func<T, bool>>(finalExpr, paramExpr);
        }

        /// <summary>
        /// 用户字符串的包含的Lambda
        /// </summary>
        /// <param name="field"></param>
        /// <param name="Value"></param>
        /// <param name="paramExpr"></param>
        /// <returns></returns>
        public static Expression GetExpressionString(string field, string Value, ParameterExpression paramExpr)
        {
            MemberExpression namePropExpr = Expression.Property(paramExpr, field);  // 创建表示属性的表达式
            System.Reflection.MethodInfo containsMethod = typeof(string).GetMethod("Contains");  // 获取表示System.String.Contains方法的System.Reflection.MethodInfo
            ConstantExpression nameValueExpr = Expression.Constant(Value, typeof(string));  // 创建表示value变量值的表达式
            MethodCallExpression nameContainsExpr = Expression.Call(namePropExpr, containsMethod, nameValueExpr);  // 创建表示参数的表达式
            return nameContainsExpr;
        }

        /// <summary>
        /// 生成Lambda表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="field"></param>
        /// <param name="Value"></param>
        /// <param name="paramExpr"></param>
        /// <param name="Condition"></param>
        /// <returns></returns>
        public static Expression GetExpression<T>(string field, object Value, ParameterExpression paramExpr, string Condition)
        {
            MemberExpression member = Expression.Property(paramExpr, field);  // 创建属性表达式
            ConstantExpression ValueExpr = Expression.Constant(Value, typeof(T));  // 创建表示变量值的表达式
            BinaryExpression query = null;// Expression.Equal(PropExpr, ValueExpr);   // 创建表示参数的表达式
            switch(Condition) {
                case "GT":  //大于
                    query = Expression.GreaterThan(member, ValueExpr);
                    break;
                case "GTE":  //大于或等于
                    query = Expression.GreaterThanOrEqual(member, ValueExpr);
                    break;
                case "LT":  //小于
                    query = Expression.LessThan(member, ValueExpr);
                    break;
                case "LTE":  //小于或等于
                    query = Expression.LessThanOrEqual(member, ValueExpr);
                    break;
                case "EQ":  //等于
                    query = Expression.Equal(member, ValueExpr);
                    break;
                case "NotEQ":  //不等于
                    query = Expression.NotEqual(member, ValueExpr);
                    break;
            }
            return query;
        }
        #endregion
    }
}

P1602 PredicateBuilder.cs 备份

trunk\CFTC.Base\CFTC.Base.Framework\DAL\Helper\PredicateBuilder.cs 的完整备份 (2016-04-21) ("钛镨膏駿竤" 的封装)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace CFTC.Base.Framework.DAL.Helper
{
    /// <summary> 
    /// Linq 多条件联合查询 
    /// 构造函数使用True时:单个AND有效,多个AND有效;单个OR无效,多个OR无效;混合时写在AND后的OR有效
    /// 构造函数使用False时:单个AND无效,多个AND无效;单个OR有效,多个OR有效;混合时写在OR后面的AND有效 
    /// </summary> 
    public static class PredicateBuilder
    {
        /// <summary>
        /// 创建一个值为true的predicate
        /// </summary>
        public static Expression<Func<T, bool>> True<T>()
        {
            return param => true;
        }

        /// <summary>
        /// 创建一个值为false的predicate
        /// </summary>
        public static Expression<Func<T, bool>> False<T>()
        {
            return param => false;
        }

        /// <summary>
        /// 从指定的lambda表达式创建一个predicate
        /// </summary>
        public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate)
        {
            return predicate;
        }

        /// <summary>
        /// 使用逻辑"和(Add)"结合第一个与第二个predicate
        /// </summary>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.AndAlso);
        }

        /// <summary>
        /// 使用逻辑"或(Or)"结合第一个与第二个predicate
        /// </summary>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.OrElse);
        }

        /// <summary>
        /// 否定 predicate.
        /// </summary>
        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
        {
            var negated = Expression.Not(expression.Body);
            return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
        }

        /// <summary>
        /// 合并两个predicate
        /// </summary>
        static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            // zip parameters (map from parameters of second to parameters of first)
            var map = first.Parameters
                .Select((f, i) => new { f, s = second.Parameters[i] })
                .ToDictionary(p => p.s, p => p.f);

            // replace parameters in the second lambda expression with the parameters in the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

            // create a merged lambda expression with parameters from the first expression
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }

        /// <summary>
        /// ParameterRebinder
        /// </summary>
        class ParameterRebinder : System.Linq.Expressions.ExpressionVisitor
        {
            /// <summary>
            /// The ParameterExpression map
            /// </summary>
            readonly Dictionary<ParameterExpression, ParameterExpression> map;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterRebinder"/> class.
            /// </summary>
            /// <param name="map">The map.</param>
            ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
            {
                this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
            }

            /// <summary>
            /// Replaces the parameters.
            /// </summary>
            /// <param name="map">The map.</param>
            /// <param name="exp">The exp.</param>
            /// <returns>Expression</returns>
            public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
            {
                return new ParameterRebinder(map).Visit(exp);
            }

            /// <summary>
            /// Visits the parameter.
            /// </summary>
            /// <param name="p">The p.</param>
            /// <returns>Expression</returns>
            protected override Expression VisitParameter(ParameterExpression p)
            {
                ParameterExpression replacement;

                if(map.TryGetValue(p, out replacement)) {
                    p = replacement;
                }

                return base.VisitParameter(p);
            }
        }
    }
}