表达式树练习实践:C#值类型、引用类型、泛型、集合、调用函数

时间:2021-10-18 00:41:46

表达式树练习实践:C#值类型、引用类型、泛型、集合、调用函数

表达式树练习实践: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 不允许。

笔者在官方文档和其他作者文章上,都没有找到具体区别是啥,去 * 搜索和查看源代码后,确定他们的区别在于 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();