一,定义变量
C# 表达式树中,定义一个变量,使用 ParameterExpression
。
创建变量结点的方法有两种,
1
2
3
|
Expression.Parameter() Expression.Variable() // 另外,定义一个常量可以使用 Expression.Constant()。 |
两种方式都是生成 ParameterExpression
类型 Parameter()
和 Variable()
都具有两个重载。他们创建一个 ParameterExpression节点,该节点可用于标识表达式树中的参数或变量。
对于使用定义:
Expression.Variable
用于在块内声明局部变量。
Expression.Parameter
用于声明输入值的参数。
先看第一种
1
2
3
4
5
6
7
8
9
|
public static ParameterExpression Parameter(Type type) { return Parameter(type, name: null ); } public static ParameterExpression Variable(Type type) { return Variable(type, name: null ); } |
从代码来看,没有区别。
再看看具有两个参数的重载
1
2
3
4
5
6
7
8
9
10
11
|
public static ParameterExpression Parameter(Type type, string name) { Validate(type, allowByRef: true ); bool byref = type.IsByRef; if (byref) { type = type.GetElementType(); } return ParameterExpression.Make(type, name, byref); } |
1
2
3
4
5
|
public static ParameterExpression Variable(Type type, string name) { Validate(type, allowByRef: false ); return ParameterExpression.Make(type, name, isByRef: false ); } |
如你所见,两者只有一个 allowByRef 出现了区别,Paramter 允许 Ref, Variable 不允许。
笔者在官方文档和其他作者文章上,都没有找到具体区别是啥,去 stackoverflow 搜索和查看源代码后,确定他们的区别在于 Variable 不能使用 ref 类型。
从字面意思来看,声明一个变量,应该用Expression.Variable
, 函数的传入参数应该使用Expression.Parameter
。
无论值类型还是引用类型,都是这样子定义。
二,访问变量/类型的属性字段和方法
访问变量或类型的属性,使用
1
|
Expression.Property() |
访问变量/类型的属性或字段,使用
1
|
Expression.PropertyOrField() |
访问变量或类型的方法,使用
1
|
Expression.Call() |
访问属性字段和方法
1
|
Expression.MakeMemberAccess |
他们都返回一个 MemberExpression类型。
使用上,根据实例化/不实例化,有个小区别,上面说了变量或类型。
意思是,已经定义的值类型或实例化的引用类型,是变量;
类型,就是指引用类型,不需要实例化的静态类型或者静态属性字段/方法。
上面的解释不太严谨,下面示例会慢慢解释。
1. 访问属性
使用 Expression.Property()
或 Expression.PropertyOrField()
调用属性。
调用静态类型属性
Console 是一个静态类型,Console.Title 可以获取编译器程序的实际位置。
1
|
Console.WriteLine(Console.Title); |
使用表达式树表达如下
1
2
3
4
5
6
7
|
MemberExpression member = Expression.Property( null , typeof (Console).GetProperty( "Title" )); Expression<Func< string >> lambda = Expression.Lambda<Func< string >>(member); string result = lambda.Compile()(); Console.WriteLine(result); Console.ReadKey(); |
因为调用的是静态类型的属性,所以第一个参数为空。
第二个参数是一个 PropertyInfo 类型。
调用实例属性/字段
C#代码如下
1
2
3
4
|
List< int > a = new List< int >() { 1, 2, 3 }; int result = a.Count; Console.WriteLine(result); Console.ReadKey(); |
在表达式树,调用实例的属性
1
2
3
4
5
6
7
8
|
ParameterExpression a = Expression.Parameter( typeof (List< int >), "a" ); MemberExpression member = Expression.Property(a, "Count" ); Expression<Func<List< int >, int >> lambda = Expression.Lambda<Func<List< int >, int >>(member, a); int result = lambda.Compile()( new List< int > { 1, 2, 3 }); Console.WriteLine(result); Console.ReadKey(); |
除了 Expression.Property() ,其他的方式请自行测试,这里不再赘述。
2. 调用函数
使用 Expression.Call()
可以调用一个静态类型的函数或者实例的函数。
调用静态类型的函数
以 Console 为例,调用 WriteLine() 方法
1
2
3
4
5
6
7
8
9
10
|
Console.WriteLine( "调用WriteLine方法" ); MethodCallExpression method = Expression.Call( null , typeof (Console).GetMethod( "WriteLine" , new Type[] { typeof ( string ) }), Expression.Constant( "调用WriteLine方法" )); Expression<Action> lambda = Expression.Lambda<Action>(method); lambda.Compile()(); Console.ReadKey(); |
Expression.Call() 的重载方法比较多,常用的重载方法是
1
|
public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments) |
因为要调用静态类型的函数,所以第一个 instance 为空(instance英文意思是实例)。
第二个 method 是要调用的重载方法。
最后一个 arguments 是传入的参数。
调用实例的函数
写一个类
1
2
3
4
5
6
7
|
public class Test { public void Print( string info) { Console.WriteLine(info); } } |
调用实例的 Printf() 方法
1
2
3
|
Test test = new Test(); test.Print( "打印出来" ); Console.ReadKey(); |
表达式表达如下
1
2
3
4
5
6
7
8
9
10
11
|
ParameterExpression a = Expression.Variable( typeof (Test), "test" ); MethodCallExpression method = Expression.Call( a, typeof (Test).GetMethod( "Print" , new Type[] { typeof ( string ) }), Expression.Constant( "打印出来" ) ); Expression<Action<Test>> lambda = Expression.Lambda<Action<Test>>(method,a); lambda.Compile()( new Test()); Console.ReadKey(); |
注意的是,Expression.Variable(typeof(Test), "test");
仅定义了一个变量,还没有初始化/赋值。对于引用类型来说,需要实例化。
上面的方式,是通过外界实例化传入里面的,后面会说如何在表达式内实例化。
三,实例化引用类型
引用类型的实例化,使用 new ,然后选择调用合适的构造函数、设置属性的值。
那么,根据上面的步骤,我们分开讨论。
new
使用 Expression.New()
来调用一个类型的构造函数。
他有五个重载,有两种常用重载:
1
2
|
public static NewExpression New(ConstructorInfo constructor); public static NewExpression New(Type type); |
依然使用上面的 Test 类型
1
|
NewExpression newA = Expression.New( typeof (Test)); |
默认没有参数的构造函数,或者只有一个构造函数,像上面这样调用。
如果像指定一个构造函数,可以
1
|
NewExpression newA = Expression.New( typeof (Test).GetConstructor(xxxxxx)); |
这里就不详细说了。
给属性赋值
实例化一个构造函数的同时,可以给属性赋值。
1
2
3
|
public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings); public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings); |
两种重载是一样的。
我们将 Test 类改成
1
2
3
4
5
6
7
8
|
public class Test { public int sample { get ; set ; } public void Print( string info) { Console.WriteLine(info); } } |
然后
1
2
3
4
|
var binding = Expression.Bind( typeof (Test).GetMember( "sample" )[0], Expression.Constant(10) ); |
创建引用类型
1
|
Expression.MemberInit() |
表示调用构造函数并初始化新对象的一个或多个成员。
如果实例化一个类,可以使用
1
2
3
4
|
NewExpression newA = Expression.New( typeof (Test)); MemberInitExpression test = Expression.MemberInit(newA, new List<MemberBinding>() { } ); |
如果要在实例化时给成员赋值
1
2
3
4
5
6
7
8
9
|
NewExpression newA = Expression.New(typeof(Test)); // 给 Test 类型的一个成员赋值 var binding = Expression.Bind( typeof(Test).GetMember("sample")[0],Expression.Constant(10)); MemberInitExpression test = Expression.MemberInit(newA, new List<MemberBinding>() { binding} ); |
示例
实例化一个类型,调用构造函数、给成员赋值,示例代码如下
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
26
27
28
29
30
|
// 调用构造函数 NewExpression newA = Expression.New( typeof (Test)); // 给 Test 类型的一个成员赋值 var binding = Expression.Bind( typeof (Test).GetMember( "sample" )[0], Expression.Constant(10)); // 实例化一个类型 MemberInitExpression test = Expression.MemberInit(newA, new List<MemberBinding>() { binding } ); // 调用方法 MethodCallExpression method1 = Expression.Call( test, typeof (Test).GetMethod( "Print" , new Type[] { typeof ( string ) }), Expression.Constant( "打印出来" ) ); // 调用属性 MemberExpression method2 = Expression.Property(test, "sample" ); Expression<Action> lambda1 = Expression.Lambda<Action>(method1); lambda1.Compile()(); Expression<Func< int >> lambda2 = Expression.Lambda<Func< int >>(method2); int sample = lambda2.Compile()(); Console.WriteLine(sample); Console.ReadKey(); |
四,实例化泛型类型于调用
将 Test 类,改成这样
1
2
3
4
5
6
7
|
public class Test<T> { public void Print<T>(T info) { Console.WriteLine(info); } } |
Test 类已经是一个泛型类,表达式实例化示例
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
26
27
|
static void Main( string [] args) { RunExpression< string >(); Console.ReadKey(); } public static void RunExpression<T>() { // 调用构造函数 NewExpression newA = Expression.New( typeof (Test<T>)); // 实例化一个类型 MemberInitExpression test = Expression.MemberInit(newA, new List<MemberBinding>() { } ); // 调用方法 MethodCallExpression method = Expression.Call( test, typeof (Test<T>).GetMethod( "Print" ).MakeGenericMethod( new Type[] { typeof (T) }), Expression.Constant( "打印出来" ) ); Expression<Action> lambda1 = Expression.Lambda<Action>(method); lambda1.Compile()(); Console.ReadKey(); } |
五,定义集合变量、初始化、添加元素
集合类型使用 ListInitExpression
表示。
创建集合类型,需要使用到
ElementInit 表示 IEnumerable集合的单个元素的初始值设定项。
ListInit 初始化一个集合。
C# 中,集合都实现了 IEnumerable,集合都具有 Add 扥方法或属性。
使用 C# 初始化一个集合并且添加元素,可以这样
1
2
3
4
5
6
|
List< string > list = new List< string >() { "a" , "b" }; list.Add( "666" ); |
而在表达式树里面,是通过 ElementInit 调用 Add 方法初始化/添加元素的。
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
MethodInfo listAdd = typeof (List< string >).GetMethod( "Add" ); /* * new List<string>() * { * "a", * "b" * }; */ ElementInit add1 = Expression.ElementInit( listAdd, Expression.Constant( "a" ), Expression.Constant( "b" ) ); // Add("666") ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant( "666" )); |
示例
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
|
MethodInfo listAdd = typeof (List< string >).GetMethod( "Add" ); ElementInit add1 = Expression.ElementInit(listAdd, Expression.Constant( "a" )); ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant( "b" )); ElementInit add3 = Expression.ElementInit(listAdd, Expression.Constant( "666" )); NewExpression list = Expression.New( typeof (List< string >)); // 初始化值 ListInitExpression setList = Expression.ListInit( list, add1, add2, add3 ); // 没啥执行的,就这样看看输出的信息 Console.WriteLine(setList.ToString()); MemberExpression member = Expression.Property(setList, "Count" ); Expression<Func< int >> lambda = Expression.Lambda<Func< int >>(member); int result = lambda.Compile()(); Console.WriteLine(result); Console.ReadKey(); |
到此这篇关于C#值类型、引用类型、泛型、集合、调用函数的表达式树实践的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://www.cnblogs.com/whuanle/p/11569083.html