目录
  • 一,定义变量
  • 二,访问变量/类型的属性字段和方法
    • 1. 访问属性
      • 调用静态类型属性
      • 调用实例属性/字段
    • 2. 调用函数
      • 调用静态类型的函数
      • 调用实例的函数
  • 三,实例化引用类型
    • new
      • 给属性赋值
        • 创建引用类型
          • 示例
          • 四,实例化泛型类型于调用
            • 五,定义集合变量、初始化、添加元素

              一,定义变量

              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&lt;MemberBinding&gt;() { 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();

               到此这篇关于C#值类型、引用类型、泛型、集合、调用函数的表达式树实践的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。

              声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。