目录
一, 定义变量
二, 访问变量 / 类型的属性字段和方法
1. 访问属性
2. 调用函数
三, 实例化引用类型
四, 实例化泛型类型于调用
五, 定义集合变量, 初始化, 添加元素
一, 定义变量
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 不允许.
笔者在官方文档和其他作者文章上, 都没有找到具体区别是啥, 去 Stack Overflow 搜索和查看源代码后, 确定他们的区别在于 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();
来源: https://www.cnblogs.com/whuanle/p/11569083.html