7. 值类型、引用类型、泛型、集合、调用函数
[TOC]
img

一,定义变量

C# 表达式树中,定义一个变量,使用 ParameterExpression
创建变量结点的方法有两种,
1
Expression.Parameter()
2
Expression.Variable()
3
// 另外,定义一个常量可以使用 Expression.Constant()。
Copied!
两种方式都是生成 ParameterExpression 类型 Parameter()Variable() 都具有两个重载。他们创建一个 ParameterExpression节点,该节点可用于标识表达式树中的参数或变量。
对于使用定义:
Expression.Variable 用于在块内声明局部变量。
Expression.Parameter用于声明输入值的参数。
先看第一种
1
public static ParameterExpression Parameter(Type type)
2
{
3
return Parameter(type, name: null);
4
}
5
6
public static ParameterExpression Variable(Type type)
7
{
8
return Variable(type, name: null);
9
}
Copied!
从代码来看,没有区别。
再看看具有两个参数的重载
1
public static ParameterExpression Parameter(Type type, string name)
2
{
3
Validate(type, allowByRef: true);
4
bool byref = type.IsByRef;
5
if (byref)
6
{
7
type = type.GetElementType();
8
}
9
10
return ParameterExpression.Make(type, name, byref);
11
}
Copied!
1
public static ParameterExpression Variable(Type type, string name)
2
{
3
Validate(type, allowByRef: false);
4
return ParameterExpression.Make(type, name, isByRef: false);
5
}
Copied!
如你所见,两者只有一个 allowByRef 出现了区别,Paramter 允许 Ref, Variable 不允许。
笔者在官方文档和其他作者文章上,都没有找到具体区别是啥,去 stackoverflow 搜索和查看源代码后,确定他们的区别在于 Variable 不能使用 ref 类型。
从字面意思来看,声明一个变量,应该用Expression.Variable, 函数的传入参数应该使用Expression.Parameter
无论值类型还是引用类型,都是这样子定义。

二,访问变量/类型的属性字段和方法

访问变量或类型的属性,使用
1
Expression.Property()
Copied!
访问变量/类型的属性或字段,使用
1
Expression.PropertyOrField()
Copied!
访问变量或类型的方法,使用
1
Expression.Call()
Copied!
访问属性字段和方法
1
Expression.MakeMemberAccess
Copied!
他们都返回一个 MemberExpression类型。
使用上,根据实例化/不实例化,有个小区别,上面说了变量或类型。
意思是,已经定义的值类型或实例化的引用类型,是变量;
类型,就是指引用类型,不需要实例化的静态类型或者静态属性字段/方法。
上面的解释不太严谨,下面示例会慢慢解释。

1. 访问属性

使用 Expression.Property()Expression.PropertyOrField()调用属性。

调用静态类型属性

Console 是一个静态类型,Console.Title 可以获取编译器程序的实际位置。
1
Console.WriteLine(Console.Title);
Copied!
使用表达式树表达如下
1
MemberExpression member = Expression.Property(null, typeof(Console).GetProperty("Title"));
2
Expression<Func<string>> lambda = Expression.Lambda<Func<string>>(member);
3
4
string result = lambda.Compile()();
5
Console.WriteLine(result);
6
7
Console.ReadKey();
Copied!
因为调用的是静态类型的属性,所以第一个参数为空。
第二个参数是一个 PropertyInfo 类型。

调用实例属性/字段

C#代码如下
1
List<int> a = new List<int>() { 1, 2, 3 };
2
int result = a.Count;
3
Console.WriteLine(result);
4
Console.ReadKey();
Copied!
在表达式树,调用实例的属性
1
ParameterExpression a = Expression.Parameter(typeof(List<int>), "a");
2
MemberExpression member = Expression.Property(a, "Count");
3
4
Expression<Func<List<int>, int>> lambda = Expression.Lambda<Func<List<int>, int>>(member, a);
5
int result = lambda.Compile()(new List<int> { 1, 2, 3 });
6
Console.WriteLine(result);
7
8
Console.ReadKey();
Copied!
除了 Expression.Property() ,其他的方式请自行测试,这里不再赘述。

2. 调用函数

使用 Expression.Call() 可以调用一个静态类型的函数或者实例的函数。

调用静态类型的函数

以 Console 为例,调用 WriteLine() 方法
1
Console.WriteLine("调用WriteLine方法");
2
3
MethodCallExpression method = Expression.Call(
4
null,
5
typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
6
Expression.Constant("调用WriteLine方法"));
7
8
Expression<Action> lambda = Expression.Lambda<Action>(method);
9
lambda.Compile()();
10
Console.ReadKey();
Copied!
Expression.Call() 的重载方法比较多,常用的重载方法是
1
public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)
Copied!
因为要调用静态类型的函数,所以第一个 instance 为空(instance英文意思是实例)。
第二个 method 是要调用的重载方法。
最后一个 arguments 是传入的参数。

调用实例的函数

写一个类
1
public class Test
2
{
3
public void Print(string info)
4
{
5
Console.WriteLine(info);
6
}
7
}
Copied!
调用实例的 Printf() 方法
1
Test test = new Test();
2
test.Print("打印出来");
3
Console.ReadKey();
Copied!
表达式表达如下
1
ParameterExpression a = Expression.Variable(typeof(Test), "test");
2
3
MethodCallExpression method = Expression.Call(
4
a,
5
typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
6
Expression.Constant("打印出来")
7
);
8
9
Expression<Action<Test>> lambda = Expression.Lambda<Action<Test>>(method,a);
10
lambda.Compile()(new Test());
11
Console.ReadKey();
Copied!
注意的是,Expression.Variable(typeof(Test), "test"); 仅定义了一个变量,还没有初始化/赋值。对于引用类型来说,需要实例化。
上面的方式,是通过外界实例化传入里面的,后面会说如何在表达式内实例化。

三,实例化引用类型

引用类型的实例化,使用 new ,然后选择调用合适的构造函数、设置属性的值。
那么,根据上面的步骤,我们分开讨论。

new

使用 Expression.New()来调用一个类型的构造函数。
他有五个重载,有两种常用重载:
1
public static NewExpression New(ConstructorInfo constructor);
2
public static NewExpression New(Type type);
Copied!
依然使用上面的 Test 类型
1
NewExpression newA = Expression.New(typeof(Test));
Copied!
默认没有参数的构造函数,或者只有一个构造函数,像上面这样调用。
如果像指定一个构造函数,可以
1
NewExpression newA = Expression.New(typeof(Test).GetConstructor(xxxxxx));
Copied!
这里就不详细说了。

给属性赋值

实例化一个构造函数的同时,可以给属性赋值。
1
public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings);
2
3
public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings);
Copied!
两种重载是一样的。
我们将 Test 类改成
1
public class Test
2
{
3
public int sample { get; set; }
4
public void Print(string info)
5
{
6
Console.WriteLine(info);
7
}
8
}
Copied!
然后
1
var binding = Expression.Bind(
2
typeof(Test).GetMember("sample")[0],
3
Expression.Constant(10)
4
);
Copied!

创建引用类型

1
Expression.MemberInit()
Copied!
表示调用构造函数并初始化新对象的一个或多个成员。
如果实例化一个类,可以使用
1
NewExpression newA = Expression.New(typeof(Test));
2
MemberInitExpression test = Expression.MemberInit(newA,
3
new List<MemberBinding>() { }
4
);
Copied!
如果要在实例化时给成员赋值
1
NewExpression newA = Expression.New(typeof(Test));
2
3
// 给 Test 类型的一个成员赋值
4
var binding = Expression.Bind(
5
typeof(Test).GetMember("sample")[0],Expression.Constant(10));
6
7
MemberInitExpression test = Expression.MemberInit(newA,
8
new List<MemberBinding>() { binding}
9
);
Copied!

示例

实例化一个类型,调用构造函数、给成员赋值,示例代码如下
1
// 调用构造函数
2
NewExpression newA = Expression.New(typeof(Test));
3
4
// 给 Test 类型的一个成员赋值
5
var binding = Expression.Bind(
6
typeof(Test).GetMember("sample")[0], Expression.Constant(10));
7
8
// 实例化一个类型
9
MemberInitExpression test = Expression.MemberInit(newA,
10
new List<MemberBinding>() { binding }
11
);
12
13
// 调用方法
14
MethodCallExpression method1 = Expression.Call(
15
test,
16
typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
17
Expression.Constant("打印出来")
18
);
19
20
// 调用属性
21
MemberExpression method2 = Expression.Property(test, "sample");
22
23
Expression<Action> lambda1 = Expression.Lambda<Action>(method1);
24
lambda1.Compile()();
25
26
Expression<Func<int>> lambda2 = Expression.Lambda<Func<int>>(method2);
27
int sample = lambda2.Compile()();
28
Console.WriteLine(sample);
29
30
Console.ReadKey();
Copied!

四,实例化泛型类型于调用

将 Test 类,改成这样
1
public class Test<T>
2
{
3
public void Print<T>(T info)
4
{
5
Console.WriteLine(info);
6
}
7
}
Copied!
Test 类已经是一个泛型类,表达式实例化示例
1
static void Main(string[] args)
2
{
3
RunExpression<string>();
4
Console.ReadKey();
5
}
6
public static void RunExpression<T>()
7
{
8
// 调用构造函数
9
NewExpression newA = Expression.New(typeof(Test<T>));
10
11
// 实例化一个类型
12
MemberInitExpression test = Expression.MemberInit(newA,
13
new List<MemberBinding>() { }
14
);
15
16
// 调用方法
17
MethodCallExpression method = Expression.Call(
18
test,
19
typeof(Test<T>).GetMethod("Print").MakeGenericMethod(new Type[] { typeof(T) }),
20
Expression.Constant("打印出来")
21
);
22
23
Expression<Action> lambda1 = Expression.Lambda<Action>(method);
24
lambda1.Compile()();
25
26
Console.ReadKey();
27
}
Copied!

五,定义集合变量、初始化、添加元素

集合类型使用 ListInitExpression表示。
创建集合类型,需要使用到
ElementInit 表示 IEnumerable集合的单个元素的初始值设定项。
ListInit 初始化一个集合。
C# 中,集合都实现了 IEnumerable,集合都具有 Add 扥方法或属性。
使用 C# 初始化一个集合并且添加元素,可以这样
1
List<string> list = new List<string>()
2
{
3
"a",
4
"b"
5
};
6
list.Add("666");
Copied!
而在表达式树里面,是通过 ElementInit 调用 Add 方法初始化/添加元素的。
示例
1
MethodInfo listAdd = typeof(List<string>).GetMethod("Add");
2
3
/*
4
* new List<string>()
5
* {
6
* "a",
7
* "b"
8
* };
9
*/
10
ElementInit add1 = Expression.ElementInit(
11
listAdd,
12
Expression.Constant("a"),
13
Expression.Constant("b")
14
);
15
// Add("666")
16
ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant("666"));
Copied!
示例
1
MethodInfo listAdd = typeof(List<string>).GetMethod("Add");
2
3
ElementInit add1 = Expression.ElementInit(listAdd, Expression.Constant("a"));
4
ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant("b"));
5
ElementInit add3 = Expression.ElementInit(listAdd, Expression.Constant("666"));
6
7
NewExpression list = Expression.New(typeof(List<string>));
8
9
// 初始化值
10
ListInitExpression setList = Expression.ListInit(
11
list,
12
add1,
13
add2,
14
add3
15
);
16
// 没啥执行的,就这样看看输出的信息
17
Console.WriteLine(setList.ToString());
18
19
MemberExpression member = Expression.Property(setList, "Count");
20
21
Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(member);
22
int result = lambda.Compile()();
23
Console.WriteLine(result);
24
25
Console.ReadKey();
Copied!
Last modified 10mo ago