【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的类,下面会用到
public class Test
{
private List<string> Strings = new List<string>();
public event Action TestEvent;
public Test(int a,int b)
{
A = a;
B = b;
Strings.Add("1");
Strings.Add("2");
Strings.Add("3");
}
public string this[int Index] { get=> Strings[Index]; set=> Strings[Index]=value; }
public int A { get; set; }
public int B { get; set; } public int Add()
{
return A+B;
}
public static int AddOne()
{
return 15;
}
}
二元运算
下面的代码实现一个二元运算,首先Dynamic方法是需要CallBinder参数的,而对应的实现有如下的Binder,我们首先需要去创建对应的Binder,二元运算就使用BinaryOperation方法创建,CSharpBinderFlags是一个枚举类型,它用于指定动态绑定操作的行为,里面可以定义在动态绑定的时候需要执行的一些特殊操作,例如,运算应该在已经检查的上下文中运行,或者使用Invoke等需要使用的一些特殊操作,或者转换的时候等等。第二个参数是ExpressionType,标明我们是那一个二元运算,第三个是当前代码运行的主体类型 that indicates where this operation is used.即这个指示了这个操作被用在哪些地方。第三个是一个CSharpArgumentInfo集合,是我们创建这个站点的时候需要使用的参数数量,如果是调用方法的时候,或者获取实例属性的时候,第一个参数是为实例参数,UseCompileTimeType类型是编译期间确定类型,其中还有IsStaticType,IsRef,IsOUt等各种,供我们使用。
然后我们创建一个dynamic的Expression,传入binder,返回类型是object,然后传入需要计算的两个参数10和1,最后得到委托,运行委托即可。
CallSiteBinder binder = Binder.BinaryOperation(
CSharpBinderFlags.None,
ExpressionType.LeftShift,
typeof(Program),
new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
}); var dynamic = Expression.Dynamic(binder, typeof(object), Expression.Constant(10), Expression.Constant(1)); Func<int> func = Expression.Lambda<Func<int>>(Expression.Convert(dynamic, typeof(int))).Compile();
Console.WriteLine(func());
创建实例
从上面的Test类看到,我们定义了两个入参,可能有的人会问了为什么入参是两个Binder为什么定义了三个呢,这是因为,创建性的Binder在创建的时候 参数第一个必须是类型参数,所以此处第一个参数必须是Test的type,然后后面是Static类型的参数,
最后一个参数就是3,调用Dynamic,第二个为返回类型的参数,然后传入对应的参数即可创建对象。
static int A = 5;
var constructorBinder = Binder.InvokeConstructor(CSharpBinderFlags.None, typeof(Program), new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType,null)
});
var createInstance = Expression.Dynamic(constructorBinder, typeof(Test),
Expression.Constant(typeof(Test)),
Expression.Constant(A),
Expression.Constant(3));
var instance = Expression.Lambda<Func<Test>>(createInstance).Compile()();
调用方法
实例方法
实例方法,使用InvokeMember,第二个参数是调用的方法名称,第三个参数是参数类型,由于我没有定义参数所以为null,然后实例方法我们需要定义一个实例参数,在CSharpArgumentInfo定义,然后调用Dynamic,返回类型必须是Object,因为这块扯犊子的是他直接写死的,如果需要转只有自己到表达式树那块Convert转,调用然后生成委托,返回结果。
var invokeBinder = Binder.InvokeMember(
CSharpBinderFlags.None,
"Add",
null,
typeof(Program),
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
var invokeDynamic=Expression.Dynamic(invokeBinder, typeof(object),Expression.Constant(instance));
var returnVal = Expression.Lambda<Func<object>>(invokeDynamic).Compile()();
Console.WriteLine(returnVal);
静态方法
大体上没有区别,在参数类型需要标记为StaticType。传入的参数不再是实例,而是静态方法所属的类型下,可以看到,返回类型必须是Object,我自己在最后Convert了,源码中的Binder默认写死Object
var invokeStaticBinder = Binder.InvokeMember(
CSharpBinderFlags.None,
"AddOne",
null,
typeof(Test),
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null) });
var invokeStaticDynamic = Expression.Dynamic(invokeStaticBinder, typeof(object),Expression.Constant(typeof(Test)));
var Val = Expression.Lambda<Func<int>>(Expression.Convert(invokeStaticDynamic,typeof(int))).Compile()();
Console.WriteLine(Val);
转换
将int转换为Object类型。
var bindConvert = Binder.Convert(CSharpBinderFlags.None,typeof(object),typeof(Program));
var expressConvert = Expression.Dynamic(bindConvert,typeof(object),Expression.Constant(A));
var funcVal=Expression.Lambda<Func<object>>(expressConvert).Compile()();
Set Get属性
下面是Set,第二个参数是设置的属性名称,参数类型是实例,以及设置的属性值,最后生成委托,然后调用即可。
var bindSet = Binder.SetMember(CSharpBinderFlags.None, "A", typeof(Program), new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var setExpress = Expression.Dynamic(bindSet,typeof(void), Expression.Constant(instance),Expression.Constant(100));
var action = Expression.Lambda<Action>(setExpress).Compile();
action();
然后是Get,参数是实例的,然后返回就行了。
var bindGet = Binder.GetMember(CSharpBinderFlags.None, "A", typeof(Program), new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var getExpress = Expression.Dynamic(bindGet, typeof(object), Expression.Constant(instance));
var getFunc= Expression.Lambda<Func<object>>(getExpress).Compile()();
Console.WriteLine(getFunc);
一元运算
一元运算的ExpressionType,参数的定义,Binder和表达式树绑定,生成委托。
var NegateBinder = Binder.UnaryOperation(CSharpBinderFlags.None,ExpressionType.Negate,typeof(Program),new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
});
var NegateExpress = Expression.Dynamic(NegateBinder, typeof(object), Expression.Constant(10));
var NegateVal = Expression.Lambda<Func<object>>(NegateExpress).Compile()();
Get Set Index
先Set,第一个参数自变量,第二个为索引,第三个是具体的值,然后表达式树和Binder绑定,生成委托,调用,即可,可以看到上面Test我们定义了一个Index的。
var setIndex = Binder.SetIndex(CSharpBinderFlags.None, typeof(Test), new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var setIndexExpress = Expression.Dynamic(setIndex,typeof(void),Expression.Constant(instance),Expression.Constant(1),Expression.Constant("cxd"));
var SetIndexaction = Expression.Lambda<Action>(setIndexExpress).Compile();
SetIndexaction();
然后是get,自变量,索引,生成委托,返回索引的值。
var getIndex= Binder.GetIndex(CSharpBinderFlags.None, typeof(Program), new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var getIndexExpress = Expression.Dynamic(getIndex, typeof(object), Expression.Constant(instance), Expression.Constant(0));
var getIndexaction = Expression.Lambda<Func<object>>(getIndexExpress).Compile()();
IsEvent
判断属性是不是事件类型的,第二个是属性名称,返回值是bool。
var isevent = Binder.IsEvent(CSharpBinderFlags.None, "TestEvent", typeof(Program));//换成非Event就不行
var iseventExpress = Expression.Dynamic(isevent,typeof(bool),Expression.Constant(instance));
var res=Expression.Lambda<Func<bool>>(iseventExpress).Compile()();
Console.WriteLine(res);
Invoke
这个是用来调用委托的,我们定义一个Func的委托,可惜的是,返回值还是只能是object,然后参数参数,然后调用委托,就返回了111。
var actions= new Func<object>(()=>111);
var invokeOtherBinder = Binder.Invoke(CSharpBinderFlags.None,typeof(Program),new[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),
});
var expression = Expression.Dynamic(invokeOtherBinder, typeof(object),Expression.Constant(actions));
var ra=Expression.Lambda<Func<object>>(expression).Compile()();
结尾
下次再见,欢迎大家加群讨论,我是四川观察,感谢各位看官的支持,谢谢大家,咱们下次再见。
【c#表达式树】最完善的表达式树Expression.Dynamic的玩法的更多相关文章
- 表达式:使用API创建表达式树(3)
一.DebugInfoExpression:发出或清除调试信息的序列点. 这允许调试器在调试时突出显示正确的源代码. static void Main(string[] args) { var asm ...
- 表达式:使用API创建表达式树(2)
一.BlockExpression类:表式一个包含可在其中定义变量的表达式序列的块.是一组表达式,类似于多个委托的 += 后的效果,其返回表达式是最后一个表达式决定.以下是BlockExpressio ...
- C#3.0新增功能10 表达式树 07 翻译(转换)表达式
连载目录 [已更新最新开发文章,点击查看详细] 本篇将介绍如何访问表达式树中的每个节点,同时生成该表达式树的已修改副本. 以下是在两个重要方案中将使用的技巧. 第一种是了解表达式树表示的算法,以 ...
- C#动态构建表达式树(三)——表达式的组合
C#动态构建表达式树(三)--表达式的组合 前言 在筛选数据的过程中,可能会有这样的情况:有一些查询条件是公共的,但是根据具体的传入参数可能需要再额外增加一个条件.对于这种问题一般有两种方法: a. ...
- 表达式:使用API创建表达式树(6)
一.ConstantExpression:表示具有常量值的表达式.因为表达式应用过程中,参数据多是 Expressions 类型,算是对常量值的一种包装吧. ConstantExpression使用比 ...
- 表达式:使用API创建表达式树(5)
一.ConditionalExpression:表达式 生成如 IIF((a == b), "a和b相等", "a与b不相等") 式子. 使用: Paramet ...
- 类型:.net;问题:C#lambda表达式;结果:Lambda表达式详解
Lambda表达式详解 前言 1.天真热,程序员活着不易,星期天,也要顶着火辣辣的太阳,总结这些东西. 2.夸夸lambda吧:简化了匿名委托的使用,让你让代码更加简洁,优雅.据说它是微软自c#1 ...
- 从Trie树(字典树)谈到后缀树
转:http://blog.csdn.net/v_july_v/article/details/6897097 引言 常关注本blog的读者朋友想必看过此篇文章:从B树.B+树.B*树谈到R 树,这次 ...
- [算法]从Trie树(字典树)谈到后缀树
我是好文章的搬运工,原文来自博客园,博主July_,地址:http://www.cnblogs.com/v-July-v/archive/2011/10/22/2316412.html 从Trie树( ...
- BZOJ 3196 Tyvj 1730 二逼平衡树 ——树状数组套主席树
[题目分析] 听说是树套树.(雾) 怒写树状数组套主席树,然后就Rank1了.23333 单点修改,区间查询+k大数查询=树状数组套主席树. [代码] #include <cstdio> ...
随机推荐
- 结对作业——考研咨询APP
结对作业--考研资讯系统 102陈同学105潘同学108苏同学 (排版:Markdown) 一.需求分析(NABCD模型) 1. N(Need 需求): 1)想知道每个专业考研可以考哪个专业2)想 ...
- dotnet Core 在linux 下设置成Service
1.新建.service文件 cd /etc/systemd/system //进入改目录 touch Core.service // 新建Core服务文件 vi Core.service // 编辑 ...
- 20193314白晨阳《Python程序设计》实验四 Python综合实践
课程:<Python程序设计> 班级: 1933 姓名: 白晨阳 学号:20193314 实验教师:王志强老师 实验日期:2021年6月13日 必修/选修: 公选课 实验内容: Pytho ...
- 用JS实现一个简单的购物车小案例
该案例主要是实现的功能有:添加商品功能,将商品添加到购物车的功能还有将商品删除功能,还有就是移出购物车的功能 该案例实现的难点是将商品添加到购物车列表的时候 数量的增加,当购物车有该商品的时候就进行累 ...
- centos7查看ip地址
centos7查看ip地址 1.centos7进入终端 安装的centos7虚拟机(无图形界面):输入账号密码进入centos7 2.命令行输入 ip addr 查看 ip地址
- 【搭建】【转】搭建 yum仓库
https://blog.csdn.net/wuxingge/article/details/100761637 3.2 服务端部署 1)安装软件程序(createrepo) yum install ...
- C# 图片 等 文件 读取操作 的一点提示
源于:在读取图片时,总喜欢首先采用:Image img=Image.FromFile("");操作,这种方式由于 调用图片的程序与图片文件是通过 绝对地址关联的,会造成 当前进程或 ...
- Angular Material TreeTable Component 使用教程
一. 安装 npm i ng-material-treetable --save npm i @angular/material @angular/cdk @angular/animations -- ...
- Executors.newScheduledThreadPool()定时任务线程池
定时任务线程池是由 Timer 进化而来 jdk中的计划任务 Timer 工具类提供了以计时器或计划任务的功能来实现按指定时间或时间间隔执行任务,但由于 Timer 工具类并不是以池 pool ,而是 ...
- Python第六章实验报告
一.实验内容:<零基础学Python>第六章实例和实战,以及一道作业题 二.实验环境:IDLE Shell 3.9.7 三.实验目的和要求:掌握定义和调用函数.变量的作用域.匿名函数.参数 ...