【c#表达式树】最完善的表达式树Expression.Dynamic的玩法

表达式,玩法,Dynamic,c# · 浏览次数 : 3143

小编点评

```csharp //一元运算 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 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 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()(); //四元运算 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()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); //五元运算 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()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); //六元运算 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()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); //七元运算 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()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); //八元运算 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()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); //九元运算 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()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); var NegateFunc = Expression.Lambda<Func<object>>(NegateExpress).Compile()(); ``` 最后,记得使用简单的排版格式,方便生成内容。

正文

引言

    在我第一次写博客的时候,写的第一篇文章,就是关于表达式树的,链接: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的玩法相似的内容:

【c#表达式树】最完善的表达式树Expression.Dynamic的玩法

引言 在我第一次写博客的时候,写的第一篇文章,就是关于表达式树的,链接:https://www.cnblogs.com/1996-Chinese-Chen/p/14987967.html,其中,当时一直没有研究Expression.Dynamic的使用方法(因为网上找不到资料),就了解到是程序运行时

使用c#强大的表达式树实现对象的深克隆之解决循环引用的问题

在上一期博客里,我们提到使用使用c#强大的表达式树实现对象的深克隆,文章地址:https://www.cnblogs.com/gmmy/p/18186750。但是文章里没有解决如何实现循环引用的问题。 循环引用 在C#中,循环引用通常发生在两个或更多的对象相互持有对方的引用,从而形成一个闭环。这种情

使用c#强大的表达式树实现对象的深克隆

一、表达式树的基本概念 表达式树是一个以树状结构表示的表达式,其中每个节点都代表表达式的一部分。例如,一个算术表达式 a + b 可以被表示为一个树,其中根节点是加法运算符,它的两个子节点分别是 a 和 b。在 LINQ(语言集成查询)中,表达式树使得能够将 C# 中的查询转换成其他形式的查询,比如

C# 12 中的新增功能

新的 C# 12 功能在预览版中已经引入. 您可以使用最新的 Visual Studio 预览版或最新的 .NET 8 预览版 SDK 来尝试这些功能。以下是一些新引入的功能: 主构造函数 集合表达式 默认 Lambda 参数 任何类型的别名 内联数组 拦截器 使用nameof访问实例成员 主构造函

Linux 提权-Capabilities

本文通过 Google 翻译 Capabilities – Linux Privilege Escalation - Juggernaut-Sec 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充。 导航 0 前言 1 什么是 Capabilities ? 2 枚举 C

盘点Python 中字符串的常用操作

摘要:盘点 Python 中字符串的几个常用操作,对新手极度的友好。 本文分享自华为云社区《盘点 Python 中字符串的常用操作,对新手极度友好》,作者:TT-千叶 。 在 Python 中字符串的表达方式有四种 一对单引号一对双引号一对三个单引号一对三个双引号a = ‘abc’b= “abc”c

[转帖]echo 输出不换行-e \c

http://www.my889.com/i/1952 在shell中,echo输出会自动换行。有时候在循环中不希望echo输出换行。代码实现如下: 1 echo -e " \c" # -e 表示开启转义, \c表示不换行 脚本: 1 2 3 4 5 6 7 8 9 #!/bin/bash i=1

使用Kettle定时从数据库A刷新数据到数据库B

# 一、需求背景 由于项目场景原因,需要将A库(MySQL)中的表a、表b、表c中的数据``定时T+1`` ``增量``的同步到B库(MySQL)。这里说明一下,不是数据库的主从备份,就是普通的数据同步。经过技术调研,发现Kettle挺合适的,原因如下: 1. Kettle (数据抽取、清洗、转换、

CF437E The Child and Polygon

# The Child and Polygon 题解 > 这世界这么大,遇到了这个奇奇怪怪的题。 这道题其实可以很自然的联想到卡特兰数。 在卡特兰数的计数中,有这么一个意义:$C_n$ 表示把有 $n+2$ 条边的凸多边形分成 $n$ 个三角形的方案数。 利用这个意义可以得到 $C_n$ 的另一个递

C# 设置PDF表单不可编辑、或提取PDF表单数据

PDF表单是PDF中的可编辑区域,允许用户填写指定信息。当表单填写完成后,有时候我们可能需要将其设置为不可编辑,以保护表单内容的完整性和可靠性。或者需要从PDF表单中提取数据以便后续处理或分析。 之前文章详细介绍过如何使用免费Spire.PDF库通过C# 创建、填写表单,本文将继续介绍该免费.NET