C# 表达式树 Expression Trees知识总结
C# 知识回顾 - 表达式树 Expression Trees
目录
- 简介
- Lambda 表达式创建表达式树
- API 创建表达式树
- 解析表达式树
- 表达式树的永久性
- 编译表达式树
- 执行表达式树
- 修改表达式树
- 调试
Expression 类型的变量,则编译器可以发射代码以创建表示该 lambda 表达式的表达式树。
C# 编译器只能从表达式 lambda (或单行 lambda)生成表达式树。
下列代码示例使用关键字 Expression创建表示 lambda 表达式:
1 Expressionint>> actionExpression = n => Console.WriteLine(n);
2 Expressionint, bool>> funcExpression1 = (n) => n < 0;
3 Expressionint, int, bool>> funcExpression2 = (n, m) => n - m == 0;
Expression 类
下列代码示例展示如何通过 API 创建表示 lambda 表达式:num => num == 0
1 //通过 Expression 类创建表达式树
2 // lambda:num => num == 0
3 ParameterExpression pExpression = Expression.Parameter(typeof(int)); //参数:num
4 ConstantExpression cExpression = Expression.Constant(0); //常量:0
5 BinaryExpression bExpression = Expression.MakeBinary(ExpressionType.Equal, pExpression, cExpression); //表达式:num == 0
6 Expressionint, bool>> lambda = Expression.Lambdaint, bool>>(bExpression, pExpression); //lambda 表达式:num => num == 0
代码使用 Expression 类的静态方法进行创建。
Expression 类型提供了 Compile 方法以将表达式树表示的代码编译成可执行委托。
1 //创建表达式树
2 Expressionstring, int>> funcExpression = msg => msg.Length;
3 //表达式树编译成委托
4 var lambda = funcExpression.Compile();
5 //调用委托
6 Console.WriteLine(lambda("Hello, World!"));
7
8 //语法简化
9 Console.WriteLine(funcExpression.Compile()("Hello, World!"));
LambdaExpression 或 Expression 类型。若要执行这些表达式树,需要调用 Compile 方法来创建一个可执行委托,然后调用该委托。
1 const int n = 1;
2 const int m = 2;
3
4 //待执行的表达式树
5 BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m));
6 //创建 lambda 表达式
7 Expressionint>> funcExpression = Expression.Lambdaint>>(bExpression);
8 //编译 lambda 表达式
9 Func<int> func = funcExpression.Compile();
10
11 //执行 lambda 表达式
12 Console.WriteLine($"{n} + {m} = {func()}");
ExpressionVisitor 类,通过 Visit 方法间接调用 VisitBinary 方法将 != 替换成 ==。基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。
1 internal class Program
2 {
3 private static void Main(string[] args)
4 {
5 Expressionint, bool>> funcExpression = num => num == 0;
6 Console.WriteLine($"Source: {funcExpression}");
7
8 var visitor = new NotEqualExpressionVisitor();
9 var expression = visitor.Visit(funcExpression);
10
11 Console.WriteLine($"Modify: {expression}");
12
13 Console.Read();
14 }
15
16 ///
17 /// 不等表达式树访问器
18 ///
19 public class NotEqualExpressionVisitor : ExpressionVisitor
20 {
21 public Expression Visit(BinaryExpression node)
22 {
23 return VisitBinary(node);
24 }
25
26 protected override Expression VisitBinary(BinaryExpression node)
27 {
28 return node.NodeType == ExpressionType.Equal
29 ? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) //重新弄个表达式:用 != 代替 ==
30 : base.VisitBinary(node);
31 }
32 }
33 }
?
1
Expressionint, int, int, int>> expr = (x, y, z) => (x + y) / z;
2.编译表达式树,Expression<TDelegate> type provides the Compile method that compiles the code represented by an expression tree into an executable delegate. ">该方法将表达式树表示的代码编译成一个可执行委托
?
1
expr.Compile()(1, 2, 3)
3.IQueryable的扩展方法,WhereIn的实现
?
1
var d = list.AsQueryable().WhereIn(o => o.Id1, new int[] { 1, 2 });
完整代码:
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace MongoDBTest
{
class Program
{
static void Main(string[] args)
{
//使用LambdaExpression构建表达式树
Expressionint, int, int, int>> expr = (x, y, z) => (x + y) / z;
Console.WriteLine(expr.Compile()(1, 2, 3));
//使用LambdaExpression构建可执行的代码
Func<int, int, int, int> fun = (x, y, z) => (x + y) / z;
Console.WriteLine(fun(1, 2, 3));
//动态构建表达式树
ParameterExpression pe1 = Expression.Parameter(typeof(int), "x");
ParameterExpression pe2 = Expression.Parameter(typeof(int), "y");
ParameterExpression pe3 = Expression.Parameter(typeof(int), "z");
var body = Expression.Divide(Expression.Add(pe1, pe2), pe3);
var w = Expression.Lambdaint, int, int, int>>(body, new ParameterExpression[] { pe1, pe2, pe3 });
Console.WriteLine(w.Compile()(1, 2, 3));
List list = new List { new Entity { Id1 = 1 }, new Entity { Id1 = 2 }, new Entity { Id1 = 3 } };
var d = list.AsQueryable().WhereIn(o => o.Id1, new int[] { 1, 2 });
d.ToList().ForEach(o =>
{
Console.WriteLine(o.Id1);
});
Console.ReadKey();
}
}
public class Entity
{
public ObjectId Id;
public int Id1;
public string Name { get; set; }
}
public static class cc
{
public static IQueryable WhereIn(this IQueryable query, Expression> obj, IEnumerable values)
{
return query.Where(BuildContainsExpression(obj, values));
}
private static Expressionbool>> BuildContainsExpression(Expression> valueSelector, IEnumerable values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
var p = valueSelector.Parameters.Single();
if (!values.Any()) return e => false;
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate(Expression.Or);
return Expression.Lambdabool>>(body, p);
}
}
}
参考博客:
- C#中的表达式树
- 表达式树基础
- C#高级程序设计(九)——表达式树
- 表达式树MSDN
出处:https://www.cnblogs.com/LittleFeiHu/p/4050428.html
=======================================================================================
C#中的表达式树
本人之前从未接触过表达式树的概念,所以特意从网上找到两篇这方面的资料学习了下。本文为阅读笔记性质博客!
表达式树是.NET 3.5之后引入的,它是一个强大灵活的工具(比如用在LINQ中构造动态查询)。
先来看看Expression类的API接口:
using System.Collections.ObjectModel;
namespace System.Linq.Expressions
{
// Summary:
// Represents a strongly typed lambda expression as a data structure in the
// form of an expression tree. This class cannot be inherited.
//
// Type parameters:
// TDelegate:
// The type of the delegate that the System.Linq.Expressions.Expression
// represents.
public sealed class Expression : LambdaExpression
{
// Summary:
// Compiles the lambda expression described by the expression tree into executable
// code.
//
// Returns:
// A delegate of type TDelegate that represents the lambda expression described
// by the System.Linq.Expressions.Expression.
public TDelegate Compile();
}
}
表达式树的语法如下:
Expression> = (param) => lamdaexpresion;
我们先来看一个简单例子:
Expressionint, int, int>> expr = (x, y) => x+y;
这就是一个表达式树了。使用Expression Tree Visualizer工具(直接调试模式下看也可以,只不过没这个直观)在调试模式下查看这个表达式树(就是一个对象),如下:
可以看到表达式树主要由下面四部分组成:
1、Body 主体部分
2、Parameters 参数部分
3、NodeType 节点类型
4、Lambda表达式类型
对于前面举的例子,主体部分即x+y,参数部分即(x,y)。Lambda表达式类型是Func。注意主体部分可以是表达式,但是不能包含语句,如下这样:
Expressionint, int, int>> expr = (x, y) => { return x+y; };
会报编译错误“Lambada expression with state body cannot be converted to expression tree”:即带有语句的Lambda表达式不能转换成表达式树。
用前面的方法虽然可以创建表达式树,但是不够灵活,如果要灵活构建表达式树,可以像下面这样:
ParameterExpression exp1 = Expression.Parameter(typeof(int), "a");
ParameterExpression exp2 = Expression.Parameter(typeof(int), "b");
BinaryExpression exp = Expression.Multiply(exp1,exp2);
var lamExp = Expression.Lambdaint, int, int>>(exp, new ParameterExpression[] { exp1, exp2 });
exp1、exp2即表达式树的参数,exp是表达式树的主体。如果我利用Reflector反编译Expressionint, int, int>> expr = (x, y) => { return x+y; };得到下面的C#代码:
ParameterExpression CS$0$0000;
ParameterExpression CS$0$0001;
Expressionint, int, int>> expr = Expression.Lambdaint, int, int>>(Expression.Multiply(CS$0$0000 = Expression.Parameter(typeof(int), "x"), CS$0$0001 = Expression.Parameter(typeof(int), "y")), new ParameterExpression[] { CS$0$0000, CS$0$0001 });
可以看到它基本和上面的手动构建代码一致。再来看一个简单的例子:
Expressionbool>> filter =
cust => Equal(Property(cust,"Region"),"North");
可以用下面的代码手动构建效果等同于上面的表达式树:
// declare a parameter of type Customer named cust
ParameterExpression custParam = Expression.Parameter(
typeof(Customer), "custParam");
// compare (equality) the Region property of the
// parameter against the string constant "North"
BinaryExpression body = Expression.Equal(
Expression.Property(custParam, "Region"),
Expression.Constant("North", typeof(string)));
// formalise this as a lambda
Expressionbool>> filter =
Expression.Lambdabool>>(body, cust);
然后我们可以通过表达式树的Compile方法将表达式树编译成Lambda表达式,如下:
Funcbool> filterFunc = filter.Compile();
但是Compile调用过程涉及动态代码生成,所以出于性能考虑最好只调用一次,然后缓存起来。或者像下面这样在静态构造块中使用(也只会调用一次):
public static class Operator
{
private static readonly Func add;
public static T Add(T x, T y)
{
return add(x, y);
}
static Operator()
{
var x = Expression.Parameter(typeof(T), "x");
var y = Expression.Parameter(typeof(T), "y");
var body = Expression.Add(x, y);
add = Expression.Lambda>(
body, x, y).Compile();
}
}
Expression类包含下面几类静态方法(.NET 3.5中):
Arithmetic: Add, AddChecked, Divide, Modulo, Multiply, MultiplyChecked, Negate, NegateChecked, Power,
Subtract, SubtractChecked, UnaryPlus
Creation: Bind, ElementInit, ListBind, ListInit, MemberBind, MemberInit, New, NewArrayBounds, NewArrayInit
Bitwise: And, ExclusiveOr, LeftShift (<<), Not, Or, RightShift (>>)
Logical: AndAlso (&&), Condition (? :), Equal, GreaterThan, GreaterThanOrEqual, LessThan,
LessThanOrEqual, NotEqual, OrElse (||), TypeIs
Member Access: ArrayIndex, ArrayLength, Call, Field, Property, PropertyOrField
Other: Convert, ConvertChecked, Coalesce (??), Constant, Invoke, Lambda, Parameter, TypeAs, Quote
下面我们类似前面重载一个浅拷贝的例子(比使用反射开销小):
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var p = new Person()
{
Name = "jxq",
Age = 23
};
var shallowCopy = Operator.ShallowCopy(p);
shallowCopy.Name = "feichexia";
Console.WriteLine(shallowCopy.Name);
Console.WriteLine(p.Name);
Console.ReadKey();
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public static class Operator
{
private static readonly Func ShallowClone;
public static T ShallowCopy(T sourcObj)
{
return ShallowClone.Invoke(sourcObj);
}
static Operator()
{
var origParam = Expression.Parameter(typeof(T), "orig");
// for each read/write property on T, create a new binding
// (for the object initializer) that copies the original's value into the new object
var setProps = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
where prop.CanRead && prop.CanWrite
select (MemberBinding)Expression.Bind(prop, Expression.Property(origParam, prop));
var body = Expression.MemberInit( // object initializer
Expression.New(typeof(T)), // ctor
setProps // property assignments
);
ShallowClone = Expression.Lambda>(body, origParam).Compile();
}
}
}
}
继续看Expression.AndAlso的使用,它可以用来替代类似下面这种多条件与的情况:
Funcbool> personEqual = (person1, person2) => person1.Name == person2.Name && person1.Age == person2.Age;
if(personEqual(p1, p2))
{
Console.WriteLine("两个对象所有属性值都相等!");
}
代码如下:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var p1 = new Person()
{
Name = "jxq",
Age = 23
};
var p2 = new Person()
{
Name = "jxq",
Age = 23
};
if (Operator.ObjectPropertyEqual(p1, p2))
{
Console.WriteLine("两个对象所有属性值都相等!");
}
Console.ReadKey();
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public static class Operator
{
private static readonly Funcbool> PropsEqual;
public static bool ObjectPropertyEqual(T obj1, T obj2)
{
return PropsEqual.Invoke(obj1, obj2);
}
static Operator()
{
var x = Expression.Parameter(typeof(T), "x");
var y = Expression.Parameter(typeof(T), "y");
// 获取类型T上的可读Property
var readableProps = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
where prop.CanRead
select prop;
Expression combination = null;
foreach (var readableProp in readableProps)
{
var thisPropEqual = Expression.Equal(Expression.Property(x, readableProp),
Expression.Property(y, readableProp));
if(combination == null)
{
combination = thisPropEqual;
}
else
{
combination = Expression.AndAlso(combination, thisPropEqual);
}
}
if(combination == null) // 如果没有需要比较的东西,直接返回false
{
PropsEqual = (p1, p2) => false;
}
else
{
PropsEqual = Expression.Lambdabool>>(combination, x, y).Compile();
}
}
}
}
}
在.NET 4.0中扩展了一些Expression的静态方法,使得编写动态代码更容易:
Mutation: AddAssign, AddAssignChecked, AndAssign, Assign, DivideAssign, ExclusiveOrAssign, LeftShiftAssign, ModuloAssign, MultiplyAssign, MultiplyAssignChecked, OrAssign, PostDecrementAssign, PostIncrementAssign, PowerAssign, PreDecrementAssign, PreIncrementAssign, RightShiftAssign, SubtractAssign, SubtractAssignChecked
Arithmetic: Decrement, Default, Increment, OnesComplement
Member Access: ArrayAccess, Dynamic
Logical: ReferenceEqual, ReferenceNotEqual, TypeEqual
Flow: Block, Break, Continue, Empty, Goto, IfThen, IfThenElse, IfFalse, IfTrue, Label, Loop, Return, Switch, SwitchCase, Unbox, Variable
Exceptions: Catch, Rethrow, Throw
Debug: ClearDebugInfo, DebugInfo
下面是一个利用表达式树编写动态代码的例子(循环打印0到9):
using System;
using System.Linq.Expressions;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var exitFor = Expression.Label("exitFor"); // jump label
var x = Expression.Variable(typeof(int), "x");
var body =
Expression.Block(
new[] { x }, // declare scope variables
Expression.Assign(x, Expression.Constant(0, typeof(int))), // init
Expression.Loop(
Expression.IfThenElse(
Expression.GreaterThanOrEqual( // test for exit
x,
Expression.Constant(10, typeof(int))
),
Expression.Break(exitFor), // perform exit
Expression.Block( // perform code
Expression.Call(
typeof(Console), "WriteLine", null, x),
Expression.PostIncrementAssign(x)
)
), exitFor
) // Loop ends
);
var runtimeLoop = Expression.Lambda(body).Compile();
runtimeLoop();
Console.Read();
}
}
}
另外WhereIn扩展实现如下,如果前面的例子都熟悉了的话,这个自然也很容易看懂了:
///
/// 使之支持Sql in语法
///
///
///
///
///
///
///
public static IQueryable WhereIn(this IQueryable query, Expression> obj, IEnumerable values)
{
return query.Where(BuildContainsExpression(obj, values));
}
private static Expressionbool>> BuildContainsExpression(
Expression> valueSelector, IEnumerable values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
var p = valueSelector.Parameters.Single();
if (!values.Any()) return e => false;
var equals = values.Select(value => (Expression) Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof (TValue))));
var body = equals.Aggregate(Expression.Or);
return Expression.Lambdabool>>(body, p);
}
调用方式如下:
db.Users.WhereIIn(u => u.Id, new int[] { 1, 2, 3 });
关于使用表达式树构建LINQ动态查询,请参考Dynamic Linq Queries with Expression Trees
参考资料:
http://www.codeproject.com/Tips/438804/Expression-Tree
http://www.infoq.com/articles/expression-compiler
出处:https://www.cnblogs.com/feichexia/archive/2013/05/28/3104832.html
C# 编译器只能从表达式 lambda (或单行 lambda)生成表达式树。
下列代码示例使用关键字 Expression创建表示 lambda 表达式:
1 Expressionint>> actionExpression = n => Console.WriteLine(n); 2 Expression int, bool>> funcExpression1 = (n) => n < 0; 3 Expression int, int, bool>> funcExpression2 = (n, m) => n - m == 0;
Expression 类
下列代码示例展示如何通过 API 创建表示 lambda 表达式:num => num == 0
1 //通过 Expression 类创建表达式树 2 // lambda:num => num == 0 3 ParameterExpression pExpression = Expression.Parameter(typeof(int)); //参数:num 4 ConstantExpression cExpression = Expression.Constant(0); //常量:0 5 BinaryExpression bExpression = Expression.MakeBinary(ExpressionType.Equal, pExpression, cExpression); //表达式:num == 0 6 Expressionint, bool>> lambda = Expression.Lambda int, bool>>(bExpression, pExpression); //lambda 表达式:num => num == 0
代码使用 Expression 类的静态方法进行创建。
Expression 类型提供了 Compile 方法以将表达式树表示的代码编译成可执行委托。
1 //创建表达式树
2 Expressionstring, int>> funcExpression = msg => msg.Length;
3 //表达式树编译成委托
4 var lambda = funcExpression.Compile();
5 //调用委托
6 Console.WriteLine(lambda("Hello, World!"));
7
8 //语法简化
9 Console.WriteLine(funcExpression.Compile()("Hello, World!"));
LambdaExpression 或 Expression 类型。若要执行这些表达式树,需要调用 Compile 方法来创建一个可执行委托,然后调用该委托。
1 const int n = 1;
2 const int m = 2;
3
4 //待执行的表达式树
5 BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m));
6 //创建 lambda 表达式
7 Expressionint>> funcExpression = Expression.Lambdaint>>(bExpression);
8 //编译 lambda 表达式
9 Func<int> func = funcExpression.Compile();
10
11 //执行 lambda 表达式
12 Console.WriteLine($"{n} + {m} = {func()}");
ExpressionVisitor 类,通过 Visit 方法间接调用 VisitBinary 方法将 != 替换成 ==。基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。
1 internal class Program
2 {
3 private static void Main(string[] args)
4 {
5 Expressionint, bool>> funcExpression = num => num == 0;
6 Console.WriteLine($"Source: {funcExpression}");
7
8 var visitor = new NotEqualExpressionVisitor();
9 var expression = visitor.Visit(funcExpression);
10
11 Console.WriteLine($"Modify: {expression}");
12
13 Console.Read();
14 }
15
16 ///
17 /// 不等表达式树访问器
18 ///
19 public class NotEqualExpressionVisitor : ExpressionVisitor
20 {
21 public Expression Visit(BinaryExpression node)
22 {
23 return VisitBinary(node);
24 }
25
26 protected override Expression VisitBinary(BinaryExpression node)
27 {
28 return node.NodeType == ExpressionType.Equal
29 ? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) //重新弄个表达式:用 != 代替 ==
30 : base.VisitBinary(node);
31 }
32 }
33 }
?
1
Expressionint, int, int, int>> expr = (x, y, z) => (x + y) / z;
2.编译表达式树,Expression<TDelegate> type provides the Compile method that compiles the code represented by an expression tree into an executable delegate. ">该方法将表达式树表示的代码编译成一个可执行委托
?
1
expr.Compile()(1, 2, 3)
3.IQueryable的扩展方法,WhereIn的实现
?
1
var d = list.AsQueryable().WhereIn(o => o.Id1, new int[] { 1, 2 });
完整代码:
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace MongoDBTest
{
class Program
{
static void Main(string[] args)
{
//使用LambdaExpression构建表达式树
Expressionint, int, int, int>> expr = (x, y, z) => (x + y) / z;
Console.WriteLine(expr.Compile()(1, 2, 3));
//使用LambdaExpression构建可执行的代码
Func<int, int, int, int> fun = (x, y, z) => (x + y) / z;
Console.WriteLine(fun(1, 2, 3));
//动态构建表达式树
ParameterExpression pe1 = Expression.Parameter(typeof(int), "x");
ParameterExpression pe2 = Expression.Parameter(typeof(int), "y");
ParameterExpression pe3 = Expression.Parameter(typeof(int), "z");
var body = Expression.Divide(Expression.Add(pe1, pe2), pe3);
var w = Expression.Lambdaint, int, int, int>>(body, new ParameterExpression[] { pe1, pe2, pe3 });
Console.WriteLine(w.Compile()(1, 2, 3));
List list = new List { new Entity { Id1 = 1 }, new Entity { Id1 = 2 }, new Entity { Id1 = 3 } };
var d = list.AsQueryable().WhereIn(o => o.Id1, new int[] { 1, 2 });
d.ToList().ForEach(o =>
{
Console.WriteLine(o.Id1);
});
Console.ReadKey();
}
}
public class Entity
{
public ObjectId Id;
public int Id1;
public string Name { get; set; }
}
public static class cc
{
public static IQueryable WhereIn(this IQueryable query, Expression> obj, IEnumerable values)
{
return query.Where(BuildContainsExpression(obj, values));
}
private static Expressionbool>> BuildContainsExpression(Expression> valueSelector, IEnumerable values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
var p = valueSelector.Parameters.Single();
if (!values.Any()) return e => false;
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate(Expression.Or);
return Expression.Lambdabool>>(body, p);
}
}
}
参考博客:
- C#中的表达式树
- 表达式树基础
- C#高级程序设计(九)——表达式树
- 表达式树MSDN
出处:https://www.cnblogs.com/LittleFeiHu/p/4050428.html
=======================================================================================
C#中的表达式树
本人之前从未接触过表达式树的概念,所以特意从网上找到两篇这方面的资料学习了下。本文为阅读笔记性质博客!
表达式树是.NET 3.5之后引入的,它是一个强大灵活的工具(比如用在LINQ中构造动态查询)。
先来看看Expression类的API接口:
using System.Collections.ObjectModel;
namespace System.Linq.Expressions
{
// Summary:
// Represents a strongly typed lambda expression as a data structure in the
// form of an expression tree. This class cannot be inherited.
//
// Type parameters:
// TDelegate:
// The type of the delegate that the System.Linq.Expressions.Expression
// represents.
public sealed class Expression : LambdaExpression
{
// Summary:
// Compiles the lambda expression described by the expression tree into executable
// code.
//
// Returns:
// A delegate of type TDelegate that represents the lambda expression described
// by the System.Linq.Expressions.Expression.
public TDelegate Compile();
}
}
表达式树的语法如下:
Expression> = (param) => lamdaexpresion;
我们先来看一个简单例子:
Expressionint, int, int>> expr = (x, y) => x+y;
这就是一个表达式树了。使用Expression Tree Visualizer工具(直接调试模式下看也可以,只不过没这个直观)在调试模式下查看这个表达式树(就是一个对象),如下:
可以看到表达式树主要由下面四部分组成:
1、Body 主体部分
2、Parameters 参数部分
3、NodeType 节点类型
4、Lambda表达式类型
对于前面举的例子,主体部分即x+y,参数部分即(x,y)。Lambda表达式类型是Func。注意主体部分可以是表达式,但是不能包含语句,如下这样:
Expressionint, int, int>> expr = (x, y) => { return x+y; };
会报编译错误“Lambada expression with state body cannot be converted to expression tree”:即带有语句的Lambda表达式不能转换成表达式树。
用前面的方法虽然可以创建表达式树,但是不够灵活,如果要灵活构建表达式树,可以像下面这样:
ParameterExpression exp1 = Expression.Parameter(typeof(int), "a");
ParameterExpression exp2 = Expression.Parameter(typeof(int), "b");
BinaryExpression exp = Expression.Multiply(exp1,exp2);
var lamExp = Expression.Lambdaint, int, int>>(exp, new ParameterExpression[] { exp1, exp2 });
exp1、exp2即表达式树的参数,exp是表达式树的主体。如果我利用Reflector反编译Expressionint, int, int>> expr = (x, y) => { return x+y; };得到下面的C#代码:
ParameterExpression CS$0$0000;
ParameterExpression CS$0$0001;
Expressionint, int, int>> expr = Expression.Lambdaint, int, int>>(Expression.Multiply(CS$0$0000 = Expression.Parameter(typeof(int), "x"), CS$0$0001 = Expression.Parameter(typeof(int), "y")), new ParameterExpression[] { CS$0$0000, CS$0$0001 });
可以看到它基本和上面的手动构建代码一致。再来看一个简单的例子:
Expressionbool>> filter =
cust => Equal(Property(cust,"Region"),"North");
可以用下面的代码手动构建效果等同于上面的表达式树:
// declare a parameter of type Customer named cust
ParameterExpression custParam = Expression.Parameter(
typeof(Customer), "custParam");
// compare (equality) the Region property of the
// parameter against the string constant "North"
BinaryExpression body = Expression.Equal(
Expression.Property(custParam, "Region"),
Expression.Constant("North", typeof(string)));
// formalise this as a lambda
Expressionbool>> filter =
Expression.Lambdabool>>(body, cust);
然后我们可以通过表达式树的Compile方法将表达式树编译成Lambda表达式,如下:
Funcbool> filterFunc = filter.Compile();
但是Compile调用过程涉及动态代码生成,所以出于性能考虑最好只调用一次,然后缓存起来。或者像下面这样在静态构造块中使用(也只会调用一次):
public static class Operator
{
private static readonly Func add;
public static T Add(T x, T y)
{
return add(x, y);
}
static Operator()
{
var x = Expression.Parameter(typeof(T), "x");
var y = Expression.Parameter(typeof(T), "y");
var body = Expression.Add(x, y);
add = Expression.Lambda>(
body, x, y).Compile();
}
}
Expression类包含下面几类静态方法(.NET 3.5中):
Arithmetic: Add, AddChecked, Divide, Modulo, Multiply, MultiplyChecked, Negate, NegateChecked, Power,
Subtract, SubtractChecked, UnaryPlus
Creation: Bind, ElementInit, ListBind, ListInit, MemberBind, MemberInit, New, NewArrayBounds, NewArrayInit
Bitwise: And, ExclusiveOr, LeftShift (<<), Not, Or, RightShift (>>)
Logical: AndAlso (&&), Condition (? :), Equal, GreaterThan, GreaterThanOrEqual, LessThan,
LessThanOrEqual, NotEqual, OrElse (||), TypeIs
Member Access: ArrayIndex, ArrayLength, Call, Field, Property, PropertyOrField
Other: Convert, ConvertChecked, Coalesce (??), Constant, Invoke, Lambda, Parameter, TypeAs, Quote
下面我们类似前面重载一个浅拷贝的例子(比使用反射开销小):
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var p = new Person()
{
Name = "jxq",
Age = 23
};
var shallowCopy = Operator.ShallowCopy(p);
shallowCopy.Name = "feichexia";
Console.WriteLine(shallowCopy.Name);
Console.WriteLine(p.Name);
Console.ReadKey();
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public static class Operator
{
private static readonly Func ShallowClone;
public static T ShallowCopy(T sourcObj)
{
return ShallowClone.Invoke(sourcObj);
}
static Operator()
{
var origParam = Expression.Parameter(typeof(T), "orig");
// for each read/write property on T, create a new binding
// (for the object initializer) that copies the original's value into the new object
var setProps = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
where prop.CanRead && prop.CanWrite
select (MemberBinding)Expression.Bind(prop, Expression.Property(origParam, prop));
var body = Expression.MemberInit( // object initializer
Expression.New(typeof(T)), // ctor
setProps // property assignments
);
ShallowClone = Expression.Lambda>(body, origParam).Compile();
}
}
}
}
继续看Expression.AndAlso的使用,它可以用来替代类似下面这种多条件与的情况:
Funcbool> personEqual = (person1, person2) => person1.Name == person2.Name && person1.Age == person2.Age;
if(personEqual(p1, p2))
{
Console.WriteLine("两个对象所有属性值都相等!");
}
代码如下:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var p1 = new Person()
{
Name = "jxq",
Age = 23
};
var p2 = new Person()
{
Name = "jxq",
Age = 23
};
if (Operator.ObjectPropertyEqual(p1, p2))
{
Console.WriteLine("两个对象所有属性值都相等!");
}
Console.ReadKey();
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public static class Operator
{
private static readonly Funcbool> PropsEqual;
public static bool ObjectPropertyEqual(T obj1, T obj2)
{
return PropsEqual.Invoke(obj1, obj2);
}
static Operator()
{
var x = Expression.Parameter(typeof(T), "x");
var y = Expression.Parameter(typeof(T), "y");
// 获取类型T上的可读Property
var readableProps = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
where prop.CanRead
select prop;
Expression combination = null;
foreach (var readableProp in readableProps)
{
var thisPropEqual = Expression.Equal(Expression.Property(x, readableProp),
Expression.Property(y, readableProp));
if(combination == null)
{
combination = thisPropEqual;
}
else
{
combination = Expression.AndAlso(combination, thisPropEqual);
}
}
if(combination == null) // 如果没有需要比较的东西,直接返回false
{
PropsEqual = (p1, p2) => false;
}
else
{
PropsEqual = Expression.Lambdabool>>(combination, x, y).Compile();
}
}
}
}
}
在.NET 4.0中扩展了一些Expression的静态方法,使得编写动态代码更容易:
Mutation: AddAssign, AddAssignChecked, AndAssign, Assign, DivideAssign, ExclusiveOrAssign, LeftShiftAssign, ModuloAssign, MultiplyAssign, MultiplyAssignChecked, OrAssign, PostDecrementAssign, PostIncrementAssign, PowerAssign, PreDecrementAssign, PreIncrementAssign, RightShiftAssign, SubtractAssign, SubtractAssignChecked
Arithmetic: Decrement, Default, Increment, OnesComplement
Member Access: ArrayAccess, Dynamic
Logical: ReferenceEqual, ReferenceNotEqual, TypeEqual
Flow: Block, Break, Continue, Empty, Goto, IfThen, IfThenElse, IfFalse, IfTrue, Label, Loop, Return, Switch, SwitchCase, Unbox, Variable
Exceptions: Catch, Rethrow, Throw
Debug: ClearDebugInfo, DebugInfo
下面是一个利用表达式树编写动态代码的例子(循环打印0到9):
using System;
using System.Linq.Expressions;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var exitFor = Expression.Label("exitFor"); // jump label
var x = Expression.Variable(typeof(int), "x");
var body =
Expression.Block(
new[] { x }, // declare scope variables
Expression.Assign(x, Expression.Constant(0, typeof(int))), // init
Expression.Loop(
Expression.IfThenElse(
Expression.GreaterThanOrEqual( // test for exit
x,
Expression.Constant(10, typeof(int))
),
Expression.Break(exitFor), // perform exit
Expression.Block( // perform code
Expression.Call(
typeof(Console), "WriteLine", null, x),
Expression.PostIncrementAssign(x)
)
), exitFor
) // Loop ends
);
var runtimeLoop = Expression.Lambda(body).Compile();
runtimeLoop();
Console.Read();
}
}
}
另外WhereIn扩展实现如下,如果前面的例子都熟悉了的话,这个自然也很容易看懂了:
///
/// 使之支持Sql in语法
///
///
///
///
///
///
///
public static IQueryable WhereIn(this IQueryable query, Expression> obj, IEnumerable values)
{
return query.Where(BuildContainsExpression(obj, values));
}
private static Expressionbool>> BuildContainsExpression(
Expression> valueSelector, IEnumerable values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
var p = valueSelector.Parameters.Single();
if (!values.Any()) return e => false;
var equals = values.Select(value => (Expression) Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof (TValue))));
var body = equals.Aggregate(Expression.Or);
return Expression.Lambdabool>>(body, p);
}
调用方式如下:
db.Users.WhereIIn(u => u.Id, new int[] { 1, 2, 3 });
关于使用表达式树构建LINQ动态查询,请参考Dynamic Linq Queries with Expression Trees
参考资料:
http://www.codeproject.com/Tips/438804/Expression-Tree
http://www.infoq.com/articles/expression-compiler
出处:https://www.cnblogs.com/feichexia/archive/2013/05/28/3104832.html
1 //创建表达式树 2 Expressionstring, int>> funcExpression = msg => msg.Length; 3 //表达式树编译成委托 4 var lambda = funcExpression.Compile(); 5 //调用委托 6 Console.WriteLine(lambda("Hello, World!")); 7 8 //语法简化 9 Console.WriteLine(funcExpression.Compile()("Hello, World!"));
LambdaExpression 或 Expression 类型。若要执行这些表达式树,需要调用 Compile 方法来创建一个可执行委托,然后调用该委托。
1 const int n = 1;
2 const int m = 2;
3
4 //待执行的表达式树
5 BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m));
6 //创建 lambda 表达式
7 Expressionint>> funcExpression = Expression.Lambdaint>>(bExpression);
8 //编译 lambda 表达式
9 Func<int> func = funcExpression.Compile();
10
11 //执行 lambda 表达式
12 Console.WriteLine($"{n} + {m} = {func()}");
ExpressionVisitor 类,通过 Visit 方法间接调用 VisitBinary 方法将 != 替换成 ==。基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。
1 internal class Program
2 {
3 private static void Main(string[] args)
4 {
5 Expressionint, bool>> funcExpression = num => num == 0;
6 Console.WriteLine($"Source: {funcExpression}");
7
8 var visitor = new NotEqualExpressionVisitor();
9 var expression = visitor.Visit(funcExpression);
10
11 Console.WriteLine($"Modify: {expression}");
12
13 Console.Read();
14 }
15
16 ///
17 /// 不等表达式树访问器
18 ///
19 public class NotEqualExpressionVisitor : ExpressionVisitor
20 {
21 public Expression Visit(BinaryExpression node)
22 {
23 return VisitBinary(node);
24 }
25
26 protected override Expression VisitBinary(BinaryExpression node)
27 {
28 return node.NodeType == ExpressionType.Equal
29 ? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) //重新弄个表达式:用 != 代替 ==
30 : base.VisitBinary(node);
31 }
32 }
33 }
?
1
Expressionint, int, int, int>> expr = (x, y, z) => (x + y) / z;
2.编译表达式树,Expression<TDelegate> type provides the Compile method that compiles the code represented by an expression tree into an executable delegate. ">该方法将表达式树表示的代码编译成一个可执行委托
?
1
expr.Compile()(1, 2, 3)
3.IQueryable的扩展方法,WhereIn的实现
?
1
var d = list.AsQueryable().WhereIn(o => o.Id1, new int[] { 1, 2 });
完整代码:
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace MongoDBTest
{
class Program
{
static void Main(string[] args)
{
//使用LambdaExpression构建表达式树
Expressionint, int, int, int>> expr = (x, y, z) => (x + y) / z;
Console.WriteLine(expr.Compile()(1, 2, 3));
//使用LambdaExpression构建可执行的代码
Func<int, int, int, int> fun = (x, y, z) => (x + y) / z;
Console.WriteLine(fun(1, 2, 3));
//动态构建表达式树
ParameterExpression pe1 = Expression.Parameter(typeof(int), "x");
ParameterExpression pe2 = Expression.Parameter(typeof(int), "y");
ParameterExpression pe3 = Expression.Parameter(typeof(int), "z");
var body = Expression.Divide(Expression.Add(pe1, pe2), pe3);
var w = Expression.Lambdaint, int, int, int>>(body, new ParameterExpression[] { pe1, pe2, pe3 });
Console.WriteLine(w.Compile()(1, 2, 3));
List list = new List { new Entity { Id1 = 1 }, new Entity { Id1 = 2 }, new Entity { Id1 = 3 } };
var d = list.AsQueryable().WhereIn(o => o.Id1, new int[] { 1, 2 });
d.ToList().ForEach(o =>
{
Console.WriteLine(o.Id1);
});
Console.ReadKey();
}
}
public class Entity
{
public ObjectId Id;
public int Id1;
public string Name { get; set; }
}
public static class cc
{
public static IQueryable WhereIn(this IQueryable query, Expression> obj, IEnumerable values)
{
return query.Where(BuildContainsExpression(obj, values));
}
private static Expressionbool>> BuildContainsExpression(Expression> valueSelector, IEnumerable values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
var p = valueSelector.Parameters.Single();
if (!values.Any()) return e => false;
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate(Expression.Or);
return Expression.Lambdabool>>(body, p);
}
}
}
参考博客:
- C#中的表达式树
- 表达式树基础
- C#高级程序设计(九)——表达式树
- 表达式树MSDN
出处:https://www.cnblogs.com/LittleFeiHu/p/4050428.html
=======================================================================================
C#中的表达式树
本人之前从未接触过表达式树的概念,所以特意从网上找到两篇这方面的资料学习了下。本文为阅读笔记性质博客!
表达式树是.NET 3.5之后引入的,它是一个强大灵活的工具(比如用在LINQ中构造动态查询)。
先来看看Expression类的API接口:
using System.Collections.ObjectModel;
namespace System.Linq.Expressions
{
// Summary:
// Represents a strongly typed lambda expression as a data structure in the
// form of an expression tree. This class cannot be inherited.
//
// Type parameters:
// TDelegate:
// The type of the delegate that the System.Linq.Expressions.Expression
// represents.
public sealed class Expression : LambdaExpression
{
// Summary:
// Compiles the lambda expression described by the expression tree into executable
// code.
//
// Returns:
// A delegate of type TDelegate that represents the lambda expression described
// by the System.Linq.Expressions.Expression.
public TDelegate Compile();
}
}
表达式树的语法如下:
Expression> = (param) => lamdaexpresion;
我们先来看一个简单例子:
Expressionint, int, int>> expr = (x, y) => x+y;
这就是一个表达式树了。使用Expression Tree Visualizer工具(直接调试模式下看也可以,只不过没这个直观)在调试模式下查看这个表达式树(就是一个对象),如下:
可以看到表达式树主要由下面四部分组成:
1、Body 主体部分
2、Parameters 参数部分
3、NodeType 节点类型
4、Lambda表达式类型
对于前面举的例子,主体部分即x+y,参数部分即(x,y)。Lambda表达式类型是Func。注意主体部分可以是表达式,但是不能包含语句,如下这样:
Expressionint, int, int>> expr = (x, y) => { return x+y; };
会报编译错误“Lambada expression with state body cannot be converted to expression tree”:即带有语句的Lambda表达式不能转换成表达式树。
用前面的方法虽然可以创建表达式树,但是不够灵活,如果要灵活构建表达式树,可以像下面这样:
ParameterExpression exp1 = Expression.Parameter(typeof(int), "a");
ParameterExpression exp2 = Expression.Parameter(typeof(int), "b");
BinaryExpression exp = Expression.Multiply(exp1,exp2);
var lamExp = Expression.Lambdaint, int, int>>(exp, new ParameterExpression[] { exp1, exp2 });
exp1、exp2即表达式树的参数,exp是表达式树的主体。如果我利用Reflector反编译Expressionint, int, int>> expr = (x, y) => { return x+y; };得到下面的C#代码:
ParameterExpression CS$0$0000;
ParameterExpression CS$0$0001;
Expressionint, int, int>> expr = Expression.Lambdaint, int, int>>(Expression.Multiply(CS$0$0000 = Expression.Parameter(typeof(int), "x"), CS$0$0001 = Expression.Parameter(typeof(int), "y")), new ParameterExpression[] { CS$0$0000, CS$0$0001 });
可以看到它基本和上面的手动构建代码一致。再来看一个简单的例子:
Expressionbool>> filter =
cust => Equal(Property(cust,"Region"),"North");
可以用下面的代码手动构建效果等同于上面的表达式树:
// declare a parameter of type Customer named cust
ParameterExpression custParam = Expression.Parameter(
typeof(Customer), "custParam");
// compare (equality) the Region property of the
// parameter against the string constant "North"
BinaryExpression body = Expression.Equal(
Expression.Property(custParam, "Region"),
Expression.Constant("North", typeof(string)));
// formalise this as a lambda
Expressionbool>> filter =
Expression.Lambdabool>>(body, cust);
然后我们可以通过表达式树的Compile方法将表达式树编译成Lambda表达式,如下:
Funcbool> filterFunc = filter.Compile();
但是Compile调用过程涉及动态代码生成,所以出于性能考虑最好只调用一次,然后缓存起来。或者像下面这样在静态构造块中使用(也只会调用一次):
public static class Operator
{
private static readonly Func add;
public static T Add(T x, T y)
{
return add(x, y);
}
static Operator()
{
var x = Expression.Parameter(typeof(T), "x");
var y = Expression.Parameter(typeof(T), "y");
var body = Expression.Add(x, y);
add = Expression.Lambda>(
body, x, y).Compile();
}
}
Expression类包含下面几类静态方法(.NET 3.5中):
Arithmetic: Add, AddChecked, Divide, Modulo, Multiply, MultiplyChecked, Negate, NegateChecked, Power,
Subtract, SubtractChecked, UnaryPlus
Creation: Bind, ElementInit, ListBind, ListInit, MemberBind, MemberInit, New, NewArrayBounds, NewArrayInit
Bitwise: And, ExclusiveOr, LeftShift (<<), Not, Or, RightShift (>>)
Logical: AndAlso (&&), Condition (? :), Equal, GreaterThan, GreaterThanOrEqual, LessThan,
LessThanOrEqual, NotEqual, OrElse (||), TypeIs
Member Access: ArrayIndex, ArrayLength, Call, Field, Property, PropertyOrField
Other: Convert, ConvertChecked, Coalesce (??), Constant, Invoke, Lambda, Parameter, TypeAs, Quote
下面我们类似前面重载一个浅拷贝的例子(比使用反射开销小):
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var p = new Person()
{
Name = "jxq",
Age = 23
};
var shallowCopy = Operator.ShallowCopy(p);
shallowCopy.Name = "feichexia";
Console.WriteLine(shallowCopy.Name);
Console.WriteLine(p.Name);
Console.ReadKey();
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public static class Operator
{
private static readonly Func ShallowClone;
public static T ShallowCopy(T sourcObj)
{
return ShallowClone.Invoke(sourcObj);
}
static Operator()
{
var origParam = Expression.Parameter(typeof(T), "orig");
// for each read/write property on T, create a new binding
// (for the object initializer) that copies the original's value into the new object
var setProps = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
where prop.CanRead && prop.CanWrite
select (MemberBinding)Expression.Bind(prop, Expression.Property(origParam, prop));
var body = Expression.MemberInit( // object initializer
Expression.New(typeof(T)), // ctor
setProps // property assignments
);
ShallowClone = Expression.Lambda>(body, origParam).Compile();
}
}
}
}
继续看Expression.AndAlso的使用,它可以用来替代类似下面这种多条件与的情况:
Funcbool> personEqual = (person1, person2) => person1.Name == person2.Name && person1.Age == person2.Age;
if(personEqual(p1, p2))
{
Console.WriteLine("两个对象所有属性值都相等!");
}
代码如下:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var p1 = new Person()
{
Name = "jxq",
Age = 23
};
var p2 = new Person()
{
Name = "jxq",
Age = 23
};
if (Operator.ObjectPropertyEqual(p1, p2))
{
Console.WriteLine("两个对象所有属性值都相等!");
}
Console.ReadKey();
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public static class Operator
{
private static readonly Funcbool> PropsEqual;
public static bool ObjectPropertyEqual(T obj1, T obj2)
{
return PropsEqual.Invoke(obj1, obj2);
}
static Operator()
{
var x = Expression.Parameter(typeof(T), "x");
var y = Expression.Parameter(typeof(T), "y");
// 获取类型T上的可读Property
var readableProps = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
where prop.CanRead
select prop;
Expression combination = null;
foreach (var readableProp in readableProps)
{
var thisPropEqual = Expression.Equal(Expression.Property(x, readableProp),
Expression.Property(y, readableProp));
if(combination == null)
{
combination = thisPropEqual;
}
else
{
combination = Expression.AndAlso(combination, thisPropEqual);
}
}
if(combination == null) // 如果没有需要比较的东西,直接返回false
{
PropsEqual = (p1, p2) => false;
}
else
{
PropsEqual = Expression.Lambdabool>>(combination, x, y).Compile();
}
}
}
}
}
在.NET 4.0中扩展了一些Expression的静态方法,使得编写动态代码更容易:
Mutation: AddAssign, AddAssignChecked, AndAssign, Assign, DivideAssign, ExclusiveOrAssign, LeftShiftAssign, ModuloAssign, MultiplyAssign, MultiplyAssignChecked, OrAssign, PostDecrementAssign, PostIncrementAssign, PowerAssign, PreDecrementAssign, PreIncrementAssign, RightShiftAssign, SubtractAssign, SubtractAssignChecked
Arithmetic: Decrement, Default, Increment, OnesComplement
Member Access: ArrayAccess, Dynamic
Logical: ReferenceEqual, ReferenceNotEqual, TypeEqual
Flow: Block, Break, Continue, Empty, Goto, IfThen, IfThenElse, IfFalse, IfTrue, Label, Loop, Return, Switch, SwitchCase, Unbox, Variable
Exceptions: Catch, Rethrow, Throw
Debug: ClearDebugInfo, DebugInfo
下面是一个利用表达式树编写动态代码的例子(循环打印0到9):
using System;
using System.Linq.Expressions;
namespace ExpressionTreeLab
{
class Program
{
static void Main(string[] args)
{
var exitFor = Expression.Label("exitFor"); // jump label
var x = Expression.Variable(typeof(int), "x");
var body =
Expression.Block(
new[] { x }, // declare scope variables
Expression.Assign(x, Expression.Constant(0, typeof(int))), // init
Expression.Loop(
Expression.IfThenElse(
Expression.GreaterThanOrEqual( // test for exit
x,
Expression.Constant(10, typeof(int))
),
Expression.Break(exitFor), // perform exit
Expression.Block( // perform code
Expression.Call(
typeof(Console), "WriteLine", null, x),
Expression.PostIncrementAssign(x)
)
), exitFor
) // Loop ends
);
var runtimeLoop = Expression.Lambda(body).Compile();
runtimeLoop();
Console.Read();
}
}
}
另外WhereIn扩展实现如下,如果前面的例子都熟悉了的话,这个自然也很容易看懂了:
///
/// 使之支持Sql in语法
///
///
///
///
///
///
///
public static IQueryable WhereIn(this IQueryable query, Expression> obj, IEnumerable values)
{
return query.Where(BuildContainsExpression(obj, values));
}
private static Expressionbool>> BuildContainsExpression(
Expression> valueSelector, IEnumerable values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
var p = valueSelector.Parameters.Single();
if (!values.Any()) return e => false;
var equals = values.Select(value => (Expression) Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof (TValue))));
var body = equals.Aggregate(Expression.Or);
return Expression.Lambdabool>>(body, p);
}
调用方式如下:
db.Users.WhereIIn(u => u.Id, new int[] { 1, 2, 3 });
关于使用表达式树构建LINQ动态查询,请参考Dynamic Linq Queries with Expression Trees
参考资料:
http://www.codeproject.com/Tips/438804/Expression-Tree
http://www.infoq.com/articles/expression-compiler
出处:https://www.cnblogs.com/feichexia/archive/2013/05/28/3104832.html
1 const int n = 1; 2 const int m = 2; 3 4 //待执行的表达式树 5 BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m)); 6 //创建 lambda 表达式 7 Expressionint>> funcExpression = Expression.Lambda int>>(bExpression); 8 //编译 lambda 表达式 9 Func<int> func = funcExpression.Compile(); 10 11 //执行 lambda 表达式 12 Console.WriteLine($"{n} + {m} = {func()}");
ExpressionVisitor 类,通过 Visit 方法间接调用 VisitBinary 方法将 != 替换成 ==。基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。
1 internal class Program
2 {
3 private static void Main(string[] args)
4 {
5 Expressionint, bool>> funcExpression = num => num == 0;
6 Console.WriteLine($"Source: {funcExpression}");
7
8 var visitor = new NotEqualExpressionVisitor();
9 var expression = visitor.Visit(funcExpression);
10
11 Console.WriteLine($"Modify: {expression}");
12
13 Console.Read();
14 }
15
16 ///
17 /// 不等表达式树访问器
18 ///
19 public class NotEqualExpressionVisitor : ExpressionVisitor
20 {
21 public Expression Visit(BinaryExpression node)
22 {
23 return VisitBinary(node);
24 }
25
26 protected override Expression VisitBinary(BinaryExpression node)
27 {
28 return node.NodeType == ExpressionType.Equal
29 ? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) //重新弄个表达式:用 != 代替 ==
30 : base.VisitBinary(node);
31 }
32 }
33 }
?
| 1 |
Expression |
2.编译表达式树,Expression<TDelegate> type provides the Compile method that compiles the code represented by an expression tree into an executable delegate. ">该方法将表达式树表示的代码编译成一个可执行委托
?| 1 |
expr.Compile()(1, 2, 3)
|
3.IQueryable
| 1 |
var d = list.AsQueryable().WhereIn(o => o.Id1, new int[] { 1, 2 });
|
完整代码:
using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.Builders; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; namespace MongoDBTest { class Program { static void Main(string[] args) { //使用LambdaExpression构建表达式树 Expressionint, int, int, int>> expr = (x, y, z) => (x + y) / z; Console.WriteLine(expr.Compile()(1, 2, 3)); //使用LambdaExpression构建可执行的代码 Func<int, int, int, int> fun = (x, y, z) => (x + y) / z; Console.WriteLine(fun(1, 2, 3)); //动态构建表达式树 ParameterExpression pe1 = Expression.Parameter(typeof(int), "x"); ParameterExpression pe2 = Expression.Parameter(typeof(int), "y"); ParameterExpression pe3 = Expression.Parameter(typeof(int), "z"); var body = Expression.Divide(Expression.Add(pe1, pe2), pe3); var w = Expression.Lambda int, int, int, int>>(body, new ParameterExpression[] { pe1, pe2, pe3 }); Console.WriteLine(w.Compile()(1, 2, 3)); List list = new List { new Entity { Id1 = 1 }, new Entity { Id1 = 2 }, new Entity { Id1 = 3 } }; var d = list.AsQueryable().WhereIn(o => o.Id1, new int[] { 1, 2 }); d.ToList().ForEach(o => { Console.WriteLine(o.Id1); }); Console.ReadKey(); } } public class Entity { public ObjectId Id; public int Id1; public string Name { get; set; } } public static class cc { public static IQueryable WhereIn (this IQueryable query, Expression > obj, IEnumerable values) { return query.Where(BuildContainsExpression(obj, values)); } private static Expression bool>> BuildContainsExpression (Expression > valueSelector, IEnumerable values) { if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); } if (null == values) { throw new ArgumentNullException("values"); } var p = valueSelector.Parameters.Single(); if (!values.Any()) return e => false; var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue)))); var body = equals.Aggregate(Expression.Or); return Expression.Lambda bool>>(body, p); } } }
参考博客:
- C#中的表达式树
- 表达式树基础
- C#高级程序设计(九)——表达式树
- 表达式树MSDN
出处:https://www.cnblogs.com/LittleFeiHu/p/4050428.html
=======================================================================================
C#中的表达式树
本人之前从未接触过表达式树的概念,所以特意从网上找到两篇这方面的资料学习了下。本文为阅读笔记性质博客!
表达式树是.NET 3.5之后引入的,它是一个强大灵活的工具(比如用在LINQ中构造动态查询)。
先来看看Expression类的API接口:
using System.Collections.ObjectModel; namespace System.Linq.Expressions { // Summary: // Represents a strongly typed lambda expression as a data structure in the // form of an expression tree. This class cannot be inherited. // // Type parameters: // TDelegate: // The type of the delegate that the System.Linq.Expressions.Expression// represents. public sealed class Expression : LambdaExpression { // Summary: // Compiles the lambda expression described by the expression tree into executable // code. // // Returns: // A delegate of type TDelegate that represents the lambda expression described // by the System.Linq.Expressions.Expression . public TDelegate Compile(); } }
表达式树的语法如下:
Expression> = (param) => lamdaexpresion;
我们先来看一个简单例子:
Expressionint, int, int>> expr = (x, y) => x+y;
这就是一个表达式树了。使用Expression Tree Visualizer工具(直接调试模式下看也可以,只不过没这个直观)在调试模式下查看这个表达式树(就是一个对象),如下:
可以看到表达式树主要由下面四部分组成:
1、Body 主体部分
2、Parameters 参数部分
3、NodeType 节点类型
4、Lambda表达式类型
对于前面举的例子,主体部分即x+y,参数部分即(x,y)。Lambda表达式类型是Func
Expression会报编译错误“Lambada expression with state body cannot be converted to expression tree”:即带有语句的Lambda表达式不能转换成表达式树。int, int, int>> expr = (x, y) => { return x+y; };
用前面的方法虽然可以创建表达式树,但是不够灵活,如果要灵活构建表达式树,可以像下面这样:
ParameterExpression exp1 = Expression.Parameter(typeof(int), "a"); ParameterExpression exp2 = Expression.Parameter(typeof(int), "b"); BinaryExpression exp = Expression.Multiply(exp1,exp2); var lamExp = Expression.Lambdaint, int, int>>(exp, new ParameterExpression[] { exp1, exp2 });
exp1、exp2即表达式树的参数,exp是表达式树的主体。如果我利用Reflector反编译Expression
ParameterExpression CS$0$0000; ParameterExpression CS$0$0001; Expressionint, int, int>> expr = Expression.Lambda int, int, int>>(Expression.Multiply(CS$0$0000 = Expression.Parameter(typeof(int), "x"), CS$0$0001 = Expression.Parameter(typeof(int), "y")), new ParameterExpression[] { CS$0$0000, CS$0$0001 });
可以看到它基本和上面的手动构建代码一致。再来看一个简单的例子:
Expressionbool>> filter = cust => Equal(Property(cust,"Region"),"North");
可以用下面的代码手动构建效果等同于上面的表达式树:
// declare a parameter of type Customer named cust ParameterExpression custParam = Expression.Parameter( typeof(Customer), "custParam"); // compare (equality) the Region property of the // parameter against the string constant "North" BinaryExpression body = Expression.Equal( Expression.Property(custParam, "Region"), Expression.Constant("North", typeof(string))); // formalise this as a lambda Expressionbool>> filter = Expression.Lambda bool>>(body, cust);
然后我们可以通过表达式树的Compile方法将表达式树编译成Lambda表达式,如下:
Funcbool> filterFunc = filter.Compile();
但是Compile调用过程涉及动态代码生成,所以出于性能考虑最好只调用一次,然后缓存起来。或者像下面这样在静态构造块中使用(也只会调用一次):
public static class Operator{ private static readonly Func add; public static T Add(T x, T y) { return add(x, y); } static Operator() { var x = Expression.Parameter(typeof(T), "x"); var y = Expression.Parameter(typeof(T), "y"); var body = Expression.Add(x, y); add = Expression.Lambda >( body, x, y).Compile(); } }
Expression类包含下面几类静态方法(.NET 3.5中):
Arithmetic: Add, AddChecked, Divide, Modulo, Multiply, MultiplyChecked, Negate, NegateChecked, Power, Subtract, SubtractChecked, UnaryPlus Creation: Bind, ElementInit, ListBind, ListInit, MemberBind, MemberInit, New, NewArrayBounds, NewArrayInit Bitwise: And, ExclusiveOr, LeftShift (<<), Not, Or, RightShift (>>) Logical: AndAlso (&&), Condition (? :), Equal, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, NotEqual, OrElse (||), TypeIs Member Access: ArrayIndex, ArrayLength, Call, Field, Property, PropertyOrField Other: Convert, ConvertChecked, Coalesce (??), Constant, Invoke, Lambda, Parameter, TypeAs, Quote
下面我们类似前面重载一个浅拷贝的例子(比使用反射开销小):
using System; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace ExpressionTreeLab { class Program { static void Main(string[] args) { var p = new Person() { Name = "jxq", Age = 23 }; var shallowCopy = Operator.ShallowCopy(p); shallowCopy.Name = "feichexia"; Console.WriteLine(shallowCopy.Name); Console.WriteLine(p.Name); Console.ReadKey(); } public class Person { public string Name { get; set; } public int Age { get; set; } } public static class Operator { private static readonly Func ShallowClone; public static T ShallowCopy(T sourcObj) { return ShallowClone.Invoke(sourcObj); } static Operator() { var origParam = Expression.Parameter(typeof(T), "orig"); // for each read/write property on T, create a new binding // (for the object initializer) that copies the original's value into the new object var setProps = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance) where prop.CanRead && prop.CanWrite select (MemberBinding)Expression.Bind(prop, Expression.Property(origParam, prop)); var body = Expression.MemberInit( // object initializer Expression.New(typeof(T)), // ctor setProps // property assignments ); ShallowClone = Expression.Lambda >(body, origParam).Compile(); } } } }
继续看Expression.AndAlso的使用,它可以用来替代类似下面这种多条件与的情况:
Funcbool> personEqual = (person1, person2) => person1.Name == person2.Name && person1.Age == person2.Age; if(personEqual(p1, p2)) { Console.WriteLine("两个对象所有属性值都相等!"); }
代码如下:
using System; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace ExpressionTreeLab { class Program { static void Main(string[] args) { var p1 = new Person() { Name = "jxq", Age = 23 }; var p2 = new Person() { Name = "jxq", Age = 23 }; if (Operator.ObjectPropertyEqual(p1, p2)) { Console.WriteLine("两个对象所有属性值都相等!"); } Console.ReadKey(); } public class Person { public string Name { get; set; } public int Age { get; set; } } public static class Operator { private static readonly Func bool> PropsEqual; public static bool ObjectPropertyEqual(T obj1, T obj2) { return PropsEqual.Invoke(obj1, obj2); } static Operator() { var x = Expression.Parameter(typeof(T), "x"); var y = Expression.Parameter(typeof(T), "y"); // 获取类型T上的可读Property var readableProps = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance) where prop.CanRead select prop; Expression combination = null; foreach (var readableProp in readableProps) { var thisPropEqual = Expression.Equal(Expression.Property(x, readableProp), Expression.Property(y, readableProp)); if(combination == null) { combination = thisPropEqual; } else { combination = Expression.AndAlso(combination, thisPropEqual); } } if(combination == null) // 如果没有需要比较的东西,直接返回false { PropsEqual = (p1, p2) => false; } else { PropsEqual = Expression.Lambda bool>>(combination, x, y).Compile(); } } } } }
在.NET 4.0中扩展了一些Expression的静态方法,使得编写动态代码更容易:
Mutation: AddAssign, AddAssignChecked, AndAssign, Assign, DivideAssign, ExclusiveOrAssign, LeftShiftAssign, ModuloAssign, MultiplyAssign, MultiplyAssignChecked, OrAssign, PostDecrementAssign, PostIncrementAssign, PowerAssign, PreDecrementAssign, PreIncrementAssign, RightShiftAssign, SubtractAssign, SubtractAssignChecked
Arithmetic: Decrement, Default, Increment, OnesComplement
Member Access: ArrayAccess, Dynamic
Logical: ReferenceEqual, ReferenceNotEqual, TypeEqual
Flow: Block, Break, Continue, Empty, Goto, IfThen, IfThenElse, IfFalse, IfTrue, Label, Loop, Return, Switch, SwitchCase, Unbox, Variable
Exceptions: Catch, Rethrow, Throw
Debug: ClearDebugInfo, DebugInfo
下面是一个利用表达式树编写动态代码的例子(循环打印0到9):
using System; using System.Linq.Expressions; namespace ExpressionTreeLab { class Program { static void Main(string[] args) { var exitFor = Expression.Label("exitFor"); // jump label var x = Expression.Variable(typeof(int), "x"); var body = Expression.Block( new[] { x }, // declare scope variables Expression.Assign(x, Expression.Constant(0, typeof(int))), // init Expression.Loop( Expression.IfThenElse( Expression.GreaterThanOrEqual( // test for exit x, Expression.Constant(10, typeof(int)) ), Expression.Break(exitFor), // perform exit Expression.Block( // perform code Expression.Call( typeof(Console), "WriteLine", null, x), Expression.PostIncrementAssign(x) ) ), exitFor ) // Loop ends ); var runtimeLoop = Expression.Lambda(body).Compile(); runtimeLoop(); Console.Read(); } } }
另外WhereIn扩展实现如下,如果前面的例子都熟悉了的话,这个自然也很容易看懂了:
////// 使之支持Sql in语法 /// /// /// /// /// /// /// public static IQueryable WhereIn (this IQueryable query, Expression > obj, IEnumerable values) { return query.Where(BuildContainsExpression(obj, values)); } private static Expression bool>> BuildContainsExpression ( Expression > valueSelector, IEnumerable values) { if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); } if (null == values) { throw new ArgumentNullException("values"); } var p = valueSelector.Parameters.Single(); if (!values.Any()) return e => false; var equals = values.Select(value => (Expression) Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof (TValue)))); var body = equals.Aggregate(Expression.Or); return Expression.Lambda bool>>(body, p); }
调用方式如下:
db.Users.WhereIIn(u => u.Id, new int[] { 1, 2, 3 });
关于使用表达式树构建LINQ动态查询,请参考Dynamic Linq Queries with Expression Trees
参考资料:
http://www.codeproject.com/Tips/438804/Expression-Tree
http://www.infoq.com/articles/expression-compiler
出处:https://www.cnblogs.com/feichexia/archive/2013/05/28/3104832.html