Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >表达式树练习实践:C#值类型、引用类型、泛型、集合、调用函数

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

作者头像
痴者工良
发布于 2021-04-26 02:02:26
发布于 2021-04-26 02:02:26
1K00
代码可运行
举报
文章被收录于专栏:痴者工良痴者工良
运行总次数:0
代码可运行

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

目录

一,定义变量

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

创建变量结点的方法有两种,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Expression.Parameter()
Expression.Variable()
// 另外,定义一个常量可以使用 Expression.Constant()。

两种方式都是生成 ParameterExpression 类型 Parameter()Variable() 都具有两个重载。他们创建一个 ParameterExpression节点,该节点可用于标识表达式树中的参数或变量。

对于使用定义:

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

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

先看第一种

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        public static ParameterExpression Parameter(Type type)
        {
            return Parameter(type, name: null);
        }
        
                public static ParameterExpression Variable(Type type)
        {
            return Variable(type, name: null);
        }

从代码来看,没有区别。

再看看具有两个参数的重载

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        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);
        }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        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

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

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

访问变量或类型的属性,使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Expression.Property()

访问变量/类型的属性或字段,使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Expression.PropertyOrField()

访问变量或类型的方法,使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Expression.Call()

访问属性字段和方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Expression.MakeMemberAccess

他们都返回一个 MemberExpression类型。

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

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

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

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

1. 访问属性

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

调用静态类型属性

Console 是一个静态类型,Console.Title 可以获取编译器程序的实际位置。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            Console.WriteLine(Console.Title);

使用表达式树表达如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            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#代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            List<int> a = new List<int>() { 1, 2, 3 };
            int result = a.Count;
            Console.WriteLine(result);
            Console.ReadKey();

在表达式树,调用实例的属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            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() 方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            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() 的重载方法比较多,常用的重载方法是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)

因为要调用静态类型的函数,所以第一个 instance 为空(instance英文意思是实例)。

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

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

调用实例的函数

写一个类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class Test
    {
        public void Print(string info)
        {
            Console.WriteLine(info);
        }
    }

调用实例的 Printf() 方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            Test test = new Test();
            test.Print("打印出来");
            Console.ReadKey();

表达式表达如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            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()来调用一个类型的构造函数。

他有五个重载,有两种常用重载:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public static NewExpression New(ConstructorInfo constructor);
 public static NewExpression New(Type type);

依然使用上面的 Test 类型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            NewExpression newA = Expression.New(typeof(Test));

默认没有参数的构造函数,或者只有一个构造函数,像上面这样调用。

如果像指定一个构造函数,可以

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            NewExpression newA = Expression.New(typeof(Test).GetConstructor(xxxxxx));

这里就不详细说了。

给属性赋值

实例化一个构造函数的同时,可以给属性赋值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings);

        public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings);

两种重载是一样的。

我们将 Test 类改成

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class Test
    {
        public int sample { get; set; }
        public void Print(string info)
        {
            Console.WriteLine(info);
        }
    }

然后

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            var binding = Expression.Bind(
                typeof(Test).GetMember("sample")[0],
                Expression.Constant(10)
            );
创建引用类型
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Expression.MemberInit()

表示调用构造函数并初始化新对象的一个或多个成员。

如果实例化一个类,可以使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            NewExpression newA = Expression.New(typeof(Test));
            MemberInitExpression test = Expression.MemberInit(newA,
                new List<MemberBinding>() { }
                );

如果要在实例化时给成员赋值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            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}
                );
示例

实例化一个类型,调用构造函数、给成员赋值,示例代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            // 调用构造函数
            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 类,改成这样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public class Test<T>
    {
        public void Print<T>(T info)
        {
            Console.WriteLine(info);
        }
    }

Test 类已经是一个泛型类,表达式实例化示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        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# 初始化一个集合并且添加元素,可以这样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            List<string> list = new List<string>()
            {
                "a",
                "b"
            };
            list.Add("666");

而在表达式树里面,是通过 ElementInit 调用 Add 方法初始化/添加元素的。

示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            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"));

示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            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();
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-09-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
c#表达式树入门,看这个就够了
ps:问: 反射有3种方式,一个是获取值,一个是赋值,一个是调用方法 (如构造器 静态方法 普通方法等),哪个才是性能元凶
洪移潮
2024/10/30
1330
表达式树练习实践:C# 循环与循环控制
无论是 for 还是 while ,平时编写循环时,都需要有跳出循环的判断,有时需要某个参数自增自减并且作为判断依据。
痴者工良
2021/04/26
5970
表达式树练习实践:C# 五类运算符的表达式树表达
这些运算符根据参数的多少,可以分作一元运算符、二元运算符、三元运算符。本文将围绕这些运算符,演示如何使用表达式树进行操作。
痴者工良
2021/04/26
7940
表达式树练习实践:C#判断语句
If 语句,使用 IfThen(Expression test, Expression ifTrue); 来表达
痴者工良
2021/04/26
5360
由浅入深表达式树(一)创建表达式树
  为什么要学习表达式树?表达式树是将我们原来可以直接由代码编写的逻辑以表达式的方式存储在树状的结构里,从而可以在运行时去解析这个树,然后执行,实现动态的编辑和执行代码。LINQ to SQL就是通过把表达式树翻译成SQL来实现的,所以了解表达树有助于我们更好的理解 LINQ to SQL,同时如果你有兴趣,可以用它创造出很多有意思的东西来。   表达式树是随着.NET 3.5推出的,所以现在也不算什么新技术了。但是不知道多少人是对它理解的很透彻, 在上一篇Lambda表达式的回复中就看的出大家对Lambd
用户1153966
2018/03/14
1.7K0
由浅入深表达式树(一)创建表达式树
学习ExpressionTree(做装配脑袋出的练习题)
1 // 第一题:画出下列表达式的表达式树。一开始,您很可能不知道某些操作其实也是表达式(比如取数组的运算符a[2]),不过没有关系,后面的习题将帮你验证这一点。 2 3 //-a 4 ParameterExpression e1 = Expression.Variable(typeof(int), "a"); 5 UnaryExpression u = Expression.Negate(e1);/
Jerremy
2022/05/09
1790
由浅入深表达式树(二)遍历表达式树
  为什么要学习表达式树?表达式树是将我们原来可以直接由代码编写的逻辑以表达式的方式存储在树状的结构里,从而可以在运行时去解析这个树,然后执行,实现动态的编辑和执行代码。LINQ to SQL就是通过把表达式树翻译成SQL来实现的,所以了解表达树有助于我们更好的理解 LINQ to SQL,同时如果你有兴趣,可以用它创造出很多有意思的东西来。   表达式树是随着.NET 3.5推出的,所以现在也不算什么新技术了。但是不知道多少人是对它理解的很透彻, 在上一篇Lambda表达式的回复中就看的出大家对Lambd
用户1153966
2018/03/14
1.2K0
由浅入深表达式树(二)遍历表达式树
表达式树练习实践:变量、常量与赋值
C#的基本值类型有:bool、byte、char、double、float、int、long等(C#中,数组属于引用类型)。
痴者工良
2021/04/26
5920
C#快速高效率复制对象另一种方式 表达式树
Student s = new Student() { Age = 20, Id = 1, Name = "Emrys" };
郑子铭
2023/08/29
2190
C#快速高效率复制对象另一种方式 表达式树
C# 表达式树 创建、生成、使用、lambda转成表达式树~表达式树的知识详解
笔者最近学了表达式树这一部分内容,为了加深理解,写文章巩固知识,如有错误,请评论指出~
痴者工良
2021/04/26
1.7K0
学习表达式树笔记 原
文章地址:  http://www.cnblogs.com/Ninputer/archive/2009/08/28/expression_tree1.html
申君健
2018/09/21
4490
【类型转换】使用c#实现简易的类型转换(Emit,Expression,反射)
    哈喽。大家好,好久不见,最近遇到了一个场景,就是在FrameWork的asp.net mvc中,有个系统里面使用的是EntityFramework的框架,在这个框架里,提供了一个SqlQuery的方法,这个方法很好用啊,以至于在EFCORE8里面又添加了回来,不过不知道性能怎么样,我遇到的场景是通过SqlQuery查询的时候,转换很慢,我估计那背后大概率是使用反射造成的, 因为我的查询可能有上十万,甚至更多,就导致了这个转换的过程及其耗时,以至于刚开始我是想通过Emit等方式去实现一个高性能转换,可是到最后没有去弄,因为我用了DataCommand去查询,最后循环DataReader来实现硬赋值,这样性能是最好,一下减少了好多秒,提升了80%,但也给了我一个灵感,一个实现简易的类型转换的灵感,所以在上周我就把代码写了出来,不过由于工作的忙碌,今天才开始写博客,接下来就呈上。
陈显达
2023/12/29
3390
原 Expression Trees学习
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Windows.Forms; using System.Threading; namespace Expressionss
魂祭心
2018/05/17
8340
Expression 表达式树学习整理
整理了一下表达式树的一些东西,入门足够了 先从ConstantExpression 开始一步一步的来吧  它表示具有常量值的表达式 我们选建一个控制台应用程序 ConstantExpression _constExp = Expression.Constant("aaa",typeof(string));//一个常量 //Console.Writeline("aaa"); MethodCallExpression _methodCall
lpxxn
2018/01/31
7200
Expression 表达式树学习整理
C#中的表达式树
在面向对象的程序设计中,接口是一种重要的语言特性。在 C# 中,接口(interface)是一种特殊的类型,它定义了一个类或结构体应该支持的一组方法、属性和事件。接口提供了一种可扩展和松散耦合的方式来定义程序设计的契约,常用于实现多态和组件化开发。本文将从架构师的角度深入分析 C# 中的接口类型和使用场景,并以 C# 代码实例来说明。
软件架构师Michael
2024/03/13
2260
【c#表达式树】最完善的表达式树Expression.Dynamic的玩法
    在我第一次写博客的时候,写的第一篇文章,就是关于表达式树的,链接:https://www.cnblogs.com/1996-Chinese-Chen/p/14987967.html,其中,当时一直没有研究Expression.Dynamic的使用方法(因为网上找不到资料),就了解到是程序运行时动态去构建表达式树,举个例子,例如我们需要在我们的查询条件中去构建他是等于或者不等于,这个时候,虽然我们可以定义等于或者不定于 的BinaryExpression,然后在代码中通过switch去进行判断,使用的是Equal还是NotEqual,这中间还需要我们自己去写一个switch,如果使用了Dynamic的方法,我们就只需要找到对应的ExpressionType然后传入创建Binder的方法中,在调用Dynamic方法就可以动态的实现,各种判断操作,或者其他的调用方法,灵活度比switch更高,接下来,我们就看看如何使用Expression.Dynamic方法来实现各种操作吧,一下所有代码操作需要引入Microsoft.CSharp.RuntimeBinder,nuget搜索Microsoft.CSharp即可。方便测试,我新建了一个Test的类,下面会用到
陈显达
2023/04/17
4880
【c#表达式树】最完善的表达式树Expression.Dynamic的玩法
表达式树
表达式树是一种C#中的数据结构,它以树的形式表示某些代码内部的结构。每个节点是一种称为表达式的C#对象,例如二元运算,方法调用,常量等。这种数据结构主要用于LINQ查询的内部机制和动态编程。在C#中,表达式树使在编译时表达式的结构和操作被保留下来,而不是像通常的.net代码那样被直接编译成IL。这使得你可以在运行时操作这些表达式或将它们转换成其他形式。例如,你可以将一个表达式树转换为可重用的Lambda表达式,或者用于创建动态查询。或者,你可以遍历表达式树来读取和解析表达式的结构。这种技术是.NET Framework中LINQ的基础,特别是在使用LINQ to SQL和LINQ to Entities时,因为它允许在运行时将LINQ查询表达式转换为SQL查询。
宿春磊Charles
2023/09/08
3160
C#笔记:用Expressions表达式自动生成linq查询
string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",                                "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",                             
超级大猪
2019/11/22
1.4K0
.NET面试题系列[12] - C# 3.0 LINQ的准备工作
"为了使LINQ能够正常工作,代码必须简化到它要求的程度。" - Jon Skeet
s055523
2018/09/14
1.2K0
表达式树练习实践:入门基础
你可以对表达式树中的代码进行编辑和运算。 这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。
痴者工良
2021/04/26
3120
推荐阅读
相关推荐
c#表达式树入门,看这个就够了
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验