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

目录
  • 一,定义变量
  • 二,访问变量/类型的属性字段和方法
    • 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#值类型、引用类型、泛型、集合、调用函数的表达式树实践的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#循环与循环控制的表达式树实现

    C# 提供了以下几种循环类型. 循环类型 描述 while 循环 当给定条件为真时,重复语句或语句组.它会在执行循环主体之前测试条件. for/foreach 循环 多次执行一个语句序列,简化管理循环变量的代码. do...while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似. 嵌套循环 您可以在 while.for 或 do..while 循环内使用一个或多个循环. 当然,还有以下用于控制循环的语句 控制语句 描述 break 语句 终止 loop 或 switch

  • C#表达式树Expression基础讲解

    什么是表达式树 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等.可以对表达式树中的代码进行编辑和运算. 这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL). 这段话是来自官网( [表达式树 (C#) | Microsoft Docs](

  • C#五类运算符使用表达式树进行操作

    在 C# 中,算术运算符,有以下类型 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 这些运算符根据参数的多少,可以分作一元运算符.二元运算符.三元运算符.本文将围绕这些运算符,演示如何使用表达式树进行操作. 对于一元运算符和二元运算符的 Expression 的子类型如下: UnaryExpression; //一元运算表达式 BinaryExpression; //二元运算表达式 一,算术运算符 运算符 描述 + 把两个操作数相加 - 从第一个操作数中减去第二个操作数

  • 浅谈c#表达式树Expression简单类型比较demo

    实例如下: using System; using System.Linq.Expressions; class DynamicPredicate { public static Expression<Func<T, T, bool>> Generate<T>(string op) { ParameterExpression x = Expression.Parameter(typeof(T), "x"); ParameterExpression y

  • C# 表达式树Expression Trees的知识梳理

    目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达式树 调试 简介 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等. 你可以对表达式树中的代码进行编辑和运算.这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性. 一.

  • C#表达式树的基本用法讲解

    表达式树使用一种类似树的结构来表示代码,它的每个节点都是一个表达式,比如方法调用和x<y这样的二元运算等.我们可以对表达式树的内容进行编辑和运算,这样能够动态修改可执行代码,以及动态创建查询等.我们可以使用匿名lambda表达式或者C# API来创建表达式树. 这一系列文章,主要是对C#表达式树的一种总结,基本知识参考MSDN的内容 这部分内容可以直接到MSDN上查看,后面的几篇文章主要分享一下,在工作中碰到的应用到表达式树的部分,谨做为记录和分享. 生成表达式树 通过lambda表达式创建表达

  • C#之Expression表达式树实例

    本文实例讲述了C#之Expression表达式树,分享给大家供大家参考.具体实现方法如下: 表达式树表示树状数据结构的代码,树状结构中的每个节点都是一个表达式,例如一个方法调用或类似 x < y 的二元运算 1.利用 Lambda 表达式创建表达式树 复制代码 代码如下: Expression<Func<int, int, int, int>> expr = (x, y, z) => (x + y) / z; 2.编译表达式树,该方法将表达式树表示的代码编译成一个可执行

  • C#判断语句的表达式树实现

    C# 提供了以下类型的判断语句: 语句 描述 if 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成. if...else 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行. 嵌套 if 语句 您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句. switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况. 嵌套 switch 语 您可以在一个 switch 语句内使用另一个 switch 

  • C#表达式树讲解

    表达式树的概念 表达式树的创建有 Lambda法 和 组装法. 学习表达式树需要 委托.Lambda.Func<> 基础. 表达式树 形状可以参考二叉树. 可以把表达式树理解成 数学表达式. 数学表达式的所有常量.符号为表达式树的底节点.每一次计算生成的结果是一个结点,或者说他们的共同结点就是他们应该进行的运算. 生成表达式树 表达式树的创建有 Lambda表达式法 和 组装法 为了方便,这里指定生成的表达式为 ( i * j ) + ( x * y ) 他们的运算是这样的 Lambda 生成

  • C#使用表达式树实现对象复制的示例代码

    需求背景:对象复制性能优化:同时,在对象复制时,应跳过引用类型的null值复制,值类型支持值类型向可空类型的复制 using Common; using System; class Program { static void Main(string[] args) { TestClassA classA = new TestClassA() { PropA = new TestClass() { Name = "cs1" }, PropB = "c1", PropC

  • C#使用表达式树(LambdaExpression)动态更新类的属性值(示例代码)

    有看过我之前发表过的C#相关文章分享和阅读过我代码的朋友们可能会在我的代码里面经常看到各种各样的λ表达式动态拼接,C#的λ表达式树是一个好东西,也是别的语言学不来的,熟悉掌握λ表达式就能够实现各种场景的个性化操作,如动态拼接查询条件.排序方式等,也能够实现替代反射的高性能操作,比如我们常用到的IQueryable和IEnumerable,每个扩展方法就全是λ表达式树. 本文给大家分享C#使用表达式树(LambdaExpression)动态更新类的属性值的相关知识,在某些业务中会遇到需要同步两个类

  • C# Lambda表达式及Lambda表达式树的创建过程

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不爱说话,默默承受着编程的巨大压力,除了技术上的交流外,他们不愿意也不擅长和别人交流,更不乐意任何人走进他们的内心! 题外话说多了,咱进入正题: 上一节中,我们讲到:在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法.C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,La

  • C#用表达式树构建动态查询的方法

    前文介绍了C#中表达式树的基本知识,在实际中,表达式树有很多用法,这里举几个例子,说明如何使用表达式树构建动态查询,从而扩展LINQ的查询方法. 在LINQ中,只要数据源实现了IQuerable<T>接口,表达式树就可以用来表示结构化查询.比如,LINQ提供了用来查询关系数据源的IQueryable<T>接口的实现,C#编译器在执行这类数据源查询时,会在运行时生成表达式树,然后,查询会遍历表达式树的数据结构,然后将其转换成针对特定数据源的合适的查询语言. 下面的几个例子演示了如何使

  • C#表达式树基础教程

    什么是表达式树 来自微软官方文档的定义: 表达式树以树形数据结构表示代码. 它能干什么呢? 你可以对表达式树中的代码进行编辑和运算. 这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 好不好玩? 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL). 哪里有应用? ORM框架.工作流框架等,使用到 Lambda 的代码...动

随机推荐