目录

* 表达式树练习实践:C#值类型、引用类型、泛型、集合、调用函数
<https://www.cnblogs.com/whuanle/p/11569083.html#表达式树练习实践c值类型引用类型泛型集合调用函数>
* 一,定义变量 <https://www.cnblogs.com/whuanle/p/11569083.html#一定义变量>
* 二,访问变量/类型的属性字段和方法
<https://www.cnblogs.com/whuanle/p/11569083.html#二访问变量类型的属性字段和方法>
* 1. 访问属性 <https://www.cnblogs.com/whuanle/p/11569083.html#访问属性>
* 2. 调用函数 <https://www.cnblogs.com/whuanle/p/11569083.html#调用函数>
* 三,实例化引用类型 <https://www.cnblogs.com/whuanle/p/11569083.html#三实例化引用类型>
* 四,实例化泛型类型于调用 <https://www.cnblogs.com/whuanle/p/11569083.html#四实例化泛型类型于调用>
* 五,定义集合变量、初始化、添加元素
<https://www.cnblogs.com/whuanle/p/11569083.html#五定义集合变量初始化添加元素>
表达式树练习实践:C#值类型、引用类型、泛型、集合、调用函数



一,定义变量

C# 表达式树中,定义一个变量,使用 ParameterExpression。

创建变量结点的方法有两种,
Expression.Parameter() Expression.Variable() // 另外,定义一个常量可以使用
Expression.Constant()。
两种方式都是生成 ParameterExpression 类型 Parameter() 和 Variable() 都具有两个重载。他们创建一个
ParameterExpression节点,该节点可用于标识表达式树中的参数或变量。

对于使用定义:

Expression.Variable 用于在块内声明局部变量。

Expression.Parameter用于声明输入值的参数。

先看第一种
public static ParameterExpression Parameter(Type type) { return
Parameter(type, name: null); } public static ParameterExpression Variable(Type
type) { return Variable(type, name: null); }
从代码来看,没有区别。

再看看具有两个参数的重载
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); }
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。

无论值类型还是引用类型,都是这样子定义。

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

访问变量或类型的属性,使用
Expression.Property()
访问变量/类型的属性或字段,使用
Expression.PropertyOrField()
访问变量或类型的方法,使用
Expression.Call()
访问属性字段和方法
Expression.MakeMemberAccess
他们都返回一个 MemberExpression类型。

使用上,根据实例化/不实例化,有个小区别,上面说了变量或类型。

意思是,已经定义的值类型或实例化的引用类型,是变量;

类型,就是指引用类型,不需要实例化的静态类型或者静态属性字段/方法。

上面的解释不太严谨,下面示例会慢慢解释。

1. 访问属性

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

调用静态类型属性

Console 是一个静态类型,Console.Title 可以获取编译器程序的实际位置。
Console.WriteLine(Console.Title);
使用表达式树表达如下
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#代码如下
List<int> a = new List<int>() { 1, 2, 3 }; int result = a.Count;
Console.WriteLine(result); Console.ReadKey();
在表达式树,调用实例的属性
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() 方法
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() 的重载方法比较多,常用的重载方法是
public static MethodCallExpression Call(Expression instance, MethodInfo
method, params Expression[] arguments)
因为要调用静态类型的函数,所以第一个 instance 为空(instance英文意思是实例)。

第二个 method 是要调用的重载方法。

最后一个 arguments 是传入的参数。

调用实例的函数

写一个类
public class Test { public void Print(string info) { Console.WriteLine(info);
} }
调用实例的 Printf() 方法
Test test = new Test(); test.Print("打印出来"); Console.ReadKey();
表达式表达如下
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()来调用一个类型的构造函数。

他有五个重载,有两种常用重载:
public static NewExpression New(ConstructorInfo constructor); public static
NewExpression New(Type type);
依然使用上面的 Test 类型
NewExpression newA = Expression.New(typeof(Test));
默认没有参数的构造函数,或者只有一个构造函数,像上面这样调用。

如果像指定一个构造函数,可以
NewExpression newA = Expression.New(typeof(Test).GetConstructor(xxxxxx));
这里就不详细说了。

给属性赋值

实例化一个构造函数的同时,可以给属性赋值。
public static MemberInitExpression MemberInit(NewExpression newExpression,
IEnumerable<MemberBinding> bindings); public static MemberInitExpression
MemberInit(NewExpression newExpression, params MemberBinding[] bindings);
两种重载是一样的。

我们将 Test 类改成
public class Test { public int sample { get; set; } public void Print(string
info) { Console.WriteLine(info); } }
然后
var binding = Expression.Bind( typeof(Test).GetMember("sample")[0],
Expression.Constant(10) );
创建引用类型
Expression.MemberInit()
表示调用构造函数并初始化新对象的一个或多个成员。

如果实例化一个类,可以使用
NewExpression newA = Expression.New(typeof(Test)); MemberInitExpression test
= Expression.MemberInit(newA, new List<MemberBinding>() { } );
如果要在实例化时给成员赋值
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} );
示例

实例化一个类型,调用构造函数、给成员赋值,示例代码如下
// 调用构造函数 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 类,改成这样
public class Test<T> { public void Print<T>(T info) {
Console.WriteLine(info); } }
Test 类已经是一个泛型类,表达式实例化示例
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# 初始化一个集合并且添加元素,可以这样
List<string> list = new List<string>() { "a", "b" }; list.Add("666");
而在表达式树里面,是通过 ElementInit 调用 Add 方法初始化/添加元素的。

示例
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"));
示例
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();