λ表达式树、lambda、委托它们之间的区别和联系

  1. 委托是一种类型,是方法的抽象,通过委托可以将方法以参数的形式传递给另一个方法
    ,同时调用委托的时候,它所包含的方法都会被实现。委托的关键字是delegate,可以自定义委托,也可以使用内置委托Func、Action,通过简化,可以将Lambda表达式或Lambda语句赋值给委托,委托的调用包括同步调用和异步调用。

  2. 表达式树(Expression)
    是一种数据结构,表达式树也称表达式目录树,是将代码以一种抽象的方式表示成一个对象树,树中每个节点本身都是一个表达式。表达式树不是可执行代码,它是一种数据结构。可以利用Lambda表达式进行声明,Lambda表达式的规则要符合Expression中Func委托的参数规则,但Lambda语句是不能声明的。

  3. lambda是当委托只有一句话代码的时候的最简写形式。

  4. Lambda表达式不仅可以用来创建委托实例,C#编译器也能够将他们转换成表达式树。

1
2
3
4
5
6
7
8
9
10
11
12
13
//Func委托,必须要有返回值,最后一个参数为返回值,前面为输入参数
Func<int, int, int> func1 = new Func<int, int, int>((int m, int n) =>
{
return m * n + 2;
});

//对其进行最简化(Lambda表达式)
Func<int, int, int> func2 = (m, n) => m * n + 2;

//调用委托
int result1 = func1(2, 3);
int result2 = func2.Invoke(2, 3);
Console.WriteLine("结果分别为:{0},{1}", result1, result2);

常用的linq和EFCore也是使用的委托和表达式树

1
2
3
4
5
new List<int>().AsQueryable().Where(s => s == 1);//使用表达式树
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)

new List<int>().Where(s => s == 1);//使用委托
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

λ表达式树入门

常见Expression API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Expression.Add, Expression.Subtract, Expression.Multiply, Expression.Divide:二元数学操作符。

Expression.And, Expression.Or, Expression.Not:逻辑操作符。

Expression.Equal, Expression.NotEqual, Expression.GreaterThan, Expression.GreaterThanOrEqual, Expression.LessThan, Expression.LessThanOrEqual:比较操作符。

Expression.Call, Expression.Invoke:方法调用操作符。

Expression.MemberAccess, Expression.ArrayIndex:成员访问操作符。

Expression.Assign:赋值操作符。

Expression.Constant:常量表达式。

Expression.Parameter:表示方法或 Lambda 表达式的参数。

Expression.Lambda:用于创建 Lambda 表达式。

Expression.New, Expression.NewArrayInit,,Expression.NewArrayBounds:创建新对象或数组。

Expression.Convert, Expression.ConvertChecked:类型转换操作符。

Expression.Block:创建代码块。

Expression.TryCatch, Expression.TryFinally, Expression.Catch:异常处理操作符。

(a,b)=>a3+b4

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = Expression.Parameter(typeof(int), "a");//声明一个int类型的参数a
var num1 = Expression.Constant(3);//声明一个常量3
var r1 = Expression.Multiply(a, num1);//乘法表达式,a*3
var b = Expression.Parameter(typeof(int), "b");//声明一个int类型的参数b
var num2 = Expression.Constant(4);//声明一个常量4
var r2 = Expression.Multiply(b, num2);//乘法表达式,b*4
var add = Expression.Add(r1, r2);//a*3+b*4
var lambda = Expression.Lambda<Func<int, int, int>>(add, a, b);// 将表达式树组装成lambda表达式树,两个输入,一个输出,故Func<int, int, int>
var func = lambda.Compile(); //将lambda表达式树编译成Func委托
Console.WriteLine(lambda);
Console.WriteLine(func(2,3));

Expression<Func<int, int, int>> func = (a, b) => a * 3 + b * 4; //直接这样写也行
1
2
3
结果:
(a, b) => ((a * 3) + (b * 4))
18

替代反射

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyClass
{
public MyClass(int num)
{
Number = num;
}
public bool MyBool { get; set; }
public int Number { get; set; }
public string MyProperty { get; set; }
public List<string> List { get; set; }
public string SayHello(string s)
{
return s + DateTime.Now.ToString("yyyy-MM-dd");
}
}

创建实例

1
2
3
4
var newExpression = Expression.New(typeof(MyClass).GetConstructor(new[] { typeof(int) }), Expression.Constant(100)); // 有参构造函数方式
//var newExpression = Expression.New(typeof(MyClass)); //无参构造函数方式
var lambda = Expression.Lambda<Func<MyClass>>(newExpression);
var myClass = lambda.Compile()();

获取属性值

e=>e.MyProperty 使用 Expression.Property

e=>e.MyProperty表达式分析得到两部分:参数和属性,也是一种基本表达式

1
2
3
4
5
6
var e = Expression.Parameter(typeof(MyClass), "e");
var property = Expression.Property(e, nameof(MyClass.MyProperty));
var lambda = Expression.Lambda<Func<MyClass, string>>(property, e);
var func = lambda.Compile();
Console.WriteLine(lambda);
Console.WriteLine(func(new MyClass(10) { MyProperty = "fdsafdsa" }));
1
2
3
结果:
e => e.MyProperty
fdsafdsa

为属性赋值

例: e=>e.MyProperty=new List(){“sdf”}
e=>e.List=new List(){“sdf”}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var newExpression = Expression.New(typeof(MyClass).GetConstructor(new[] { typeof(int) }), Expression.Constant(100));// 有参构造函数方式
var lambda = Expression.Lambda<Func<MyClass>>(newExpression);
var myClass = lambda.Compile()();

var e = Expression.Parameter(typeof(MyClass), "e");
var property = Expression.Property(e, nameof(MyClass.MyProperty));
var assigString = Expression.Assign(property, Expression.Constant("fdsfds")); //为字符串类型的属性赋值
var assignList = Expression.Assign(Expression.Property(e, nameof(MyClass.List)),
Expression.Constant(new List<string>() { "fdsa" })); //为集合类型的属性赋值

Expression.Lambda<Action<MyClass>>(assignList, e).Compile()(myClass);// 因为赋值操作没有返回值,所以是Action,并且只有一个入参
Expression.Lambda<Action<MyClass>>(assigString, e).Compile()(myClass);

Console.WriteLine(myClass.List.Count);
Console.WriteLine(Expression.Lambda<Action<MyClass>>(assignList, e));
1
2
3
结果:
1
e => (e.List = value(System.Collections.Generic.List`1[System.String]))

调用无参方法

调用方法通过Expression.Call进行对象的方法调用,调用前需要先获取被调用的方法对象

1
2
3
4
5
6
var e = Expression.Parameter(typeof(MyClass), "e");
var method = typeof(MyClass).GetMethod(nameof(GetHashCode));//获取MyClass的GetHashCode方法
var call = Expression.Call(e, method);//e.GetHashCode()
var lambda = Expression.Lambda<Func<MyClass, int>>(call, e);
Console.WriteLine(lambda);
Console.WriteLine(lambda.Compile()(new MyClass(10)));
1
2
3
结果:
e => e.GetHashCode()
43942917

调用有参方法

1
2
3
4
var e = Expression.Parameter(typeof(MyClass), "e");
var method = typeof(MyClass).GetMethod(nameof(MyClass.SayHello), new[] { typeof(string) });
var call = Expression.Call(e, method, Expression.Constant("你好"));//调用SayHello方法并给方法传入"你好"参数
var lambda = Expression.Lambda<Func<MyClass, string>>(call, e);

调用linq拓展方法

linq拓展方法表达式树拆分图.jpg

1
2
3
4
5
6
7
8
9
var e = Expression.Parameter(typeof(MyClass), "e");
var list = Expression.Property(e, nameof(MyClass.List)); //e.List
var containsMethod = typeof(Enumerable).GetMethods()
.FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "Contains")
.MakeGenericMethod(typeof(string));
var contains = Expression.Call(containsMethod, list, Expression.Constant("s"));//e.List.Contains("s")
var lambda = Expression.Lambda<Func<MyClass, bool>>(contains, e);
Console.WriteLine(lambda);
Console.WriteLine(lambda.Compile()(new MyClass(1) { List = new List<string>('s') }));
1
2
3
结果:
e => e.List.Contains("s")
False

条件表达式

Expression.Condition,即可生成三目表达式的λ表达式树

1
2
3
4
var x = Expression.Parameter(typeof(int), "x");
var gt60 = Expression.GreaterThan(x, Expression.Constant(60));//x>60
var condition = Expression.Condition(gt60, Expression.Constant("及格"), Expression.Constant("不及格"));//x>60?"及格":"不及格"
var lambda = Expression.Lambda<Func<int, string>>(condition, x);//x=>x>60?"及格":"不及格"

null值表达式

e=>e.MyProperty??”s”

Expression.Coalesce便是对标的C#6专属的null值表达式

1
2
3
4
var e = Expression.Parameter(typeof(MyClass), "e");
var expression = Expression.Property(e, "MyProperty");
var coalesce = Expression.Coalesce(expression, Expression.Constant("s"));
var lambda = Expression.Lambda<Func<MyClass, string>>(coalesce, e);

类型转换

Convert.ToInt32(x)

Expression.Convert即等价于Convert静态类

1
2
var convert = Expression.Convert(Expression.Constant(10.234), typeof(int));
var lambda = Expression.Lambda<Func<int>>(convert);

声明一个数组对象

Expression.NewArrayBounds即可生成一个创建数组对象的表达式:

1
2
var array = Expression.NewArrayBounds(typeof(string), Expression.Constant(5));
var lambda = Expression.Lambda<Func<string[]>>(array);

实战

m=>m.MyProperty.Contains(“ldqk”)||m.List.Any(s=>s.Length>1&&s.Contains(“a”))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 前一部分
var m = Expression.Parameter(typeof(MyClass), "m");
var myProperty = Expression.Property(m, nameof(MyClass.MyProperty)); // m.MyProperty
var contains = Expression.Call(myProperty, typeof(string).GetMethod("Contains", new[] { typeof(string) }),
Expression.Constant("ldqk")); // m.MyProperty.Contains("ldqk")

//后一部分
var s = Expression.Parameter(typeof(string), "s");
var length = Expression.Property(s, nameof(string.Length)); //s.Length
var gt10 = Expression.GreaterThan(length, Expression.Constant(1)); //s=>s.Length>1
var anyConstains = Expression.Call(s,typeof(string).GetMethod("Contains", new[] { typeof(string) }), Expression.Constant("a"));// s.Contains("a")
var anyWhere = Expression.And(gt10, anyConstains); //s.Length>1&&s.Contains("a")
var anyLambda = Expression.Lambda(anyWhere, s);

var any = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "Any").MakeGenericMethod(typeof(string));
var list = Expression.Property(m, nameof(MyClass.List)); // m.List
var whereLambda = Expression.Call(null, any, list, anyLambda);

var lambda = Expression.Lambda<Func<MyClass, bool>>(Expression.OrElse(contains, whereLambda),m); //m=>m.MyProperty.Contains("ldqk")||m.List.Any(s=>s.Length>1&&s.Contains("a"))
Console.WriteLine(lambda);

链式调用s=>s.List.Select(e => e.Length).OrderBy(x=>x).FirstOrDefault() > 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var s = Expression.Parameter(typeof(MyClass), "s");
var list = Expression.Property(s, nameof(MyClass.List)); //s.List
var selectMethod = typeof(Enumerable).GetMethods()
.FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "Select")
.MakeGenericMethod(typeof(string), typeof(int));
var e = Expression.Parameter(typeof(string), "e");
var elen = Expression.Property(e, nameof(string.Length)); // e.Length
var selectLambda = Expression.Lambda<Func<string, int>>(elen, e); // e=>e.Length
var select = Expression.Call(selectMethod, list, selectLambda);//s.List.Select(e => e.Length)

var orderby = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "OrderBy").MakeGenericMethod(typeof(int), typeof(int));
var parameter = Expression.Parameter(typeof(int), "x");
var orderbyLambda = Expression.Lambda(parameter, parameter); //x=>x
var orderByCall = Expression.Call(orderby, select, orderbyLambda); //s.List.Select(e => e.Length).OrderBy(x=>x)

var first = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 1 && info.Name == "FirstOrDefault").MakeGenericMethod(typeof(int));
var firstExp = Expression.Call(null, first, orderByCall); //s.List.Select(e => e.Length).OrderBy(x=>x).FirstOrDefault()

var greaterThan = Expression.GreaterThan(firstExp, Expression.Constant(1)); //s.List.Select(e => e.Length).OrderBy(x=>x).FirstOrDefault() > 1
var lambda = Expression.Lambda<Func<MyClass, bool>>(greaterThan, s);
Console.WriteLine(lambda);