C#---Expression(表达式)

前言:Expression 是C# 高级编程,表达式的应用场景有 ORM框架:Entity Framework,Dapper等,规则引擎:动态业务规则评估, 依赖注入:高级DI容器实现,测试框架:模拟和断言, 动态查询构建:根据用户输入构建查询 等等。 学习掌握Expression 对于构建动态逻辑的应用非常有帮助。

1.Expression 的由来

为了将Linq 查询语句转换成SQL语句,在其它外部服务器上执行。将代码是给计算机执行的指令序列到代码是可以通过分析,转换和解释的数据结构。

将代码表示为数据结构,表达式架起了编译时静态世界到运行时动态世界的桥梁。

2. Expression的常用语法

2.1 Expression 特点

知道了Expression的目的,就是为了表达代码的数据结构。那么表达式的很多特点跟代码的表述形式很相近。

在学习一门语法之前,要先学习语句,变量,常量,赋值, 参数, 算术运算, 条件语句,循环语句,方法,类,对象,成员调用,异常 等等。

Expression 的类型有 NodeType 有84 种之多大多提供了表述以上代码的形式。

这里举个例子说明:

例1:想要定义一个变量,并且为这个变量赋值,打印输出这个变量的值

csharp 复制代码
ParameterExpression varExp = Expression.Variable(typeof(int), "i");

ConstantExpression conExp = Expression.Constant(10, typeof(int));

BinaryExpression assignExp = Expression.Assign(varExp, conExp);

Expression lambda = Expression.Block(new[] { varExp }, assignExp, varExp);

Expression<Func<int>> lambdaExp = Expression.Lambda<Func<int>>(lambda);

Console.WriteLine(lambdaExp.Compile().Invoke());

既然知道了表达式的目的就是表述一段代码结构,那这段代码结构是可以被编译器编译成委托,然后被执行的。

csharp 复制代码
Console.WriteLine(lambdaExp.Compile().Invoke());
  1. Expression 是所有表达式类型的基类。
  2. Expression 的类型能够编写的大多数代码结构
  3. 通过表达式语句块最终编译动态生成一个委托

2.3 Expression核心组件

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;

namespace Study01_Expression.Study1_Expression
{
    internal class _01_Expression
    {
        private static readonly Lazy<_01_Expression> _instance = new Lazy<_01_Expression>(() => new _01_Expression());

        public static _01_Expression Instance => _instance.Value;


        public void Test()
        {
            Study3();
        }

        /// <summary>
        /// 初识表达式的核心组件
        /// </summary>
        public void Study1()
        {
            // 1.表达式的基类: Expression
            // 2.参数表达式:ParameterExpression
            // 3.二元运算表达式:BinaryExpression
            // 4.常量表达式:ConsantExpression
            // 5 方法表达式:MethodCallExpression
            // 6.属性或字段表达式: MemberExpression
            // 7.Lambda表达式:LambdaExpression

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("\r\n1.表达式的基类: Expression");
            stringBuilder.Append("\r\n2.参数表达式:ParameterExpression");
            stringBuilder.Append("\r\n3.二元运算表达式:BinaryExpression");
            stringBuilder.Append("\r\n4.常量表达式:ConsantExpression");
            stringBuilder.Append("\r\n5.方法表达式:MethodCallExpression");
            stringBuilder.Append("\r\n6.属性或字段表达式: MemberExpression");
            stringBuilder.Append("\r\n7.Lambda表达式:LambdaExpression");

            Console.WriteLine(stringBuilder.ToString());
        }

        /// <summary>
        /// 7类表达式的基本使用
        /// </summary>
        public void Study2()
        {
            // 1.基本元素的创建
            // 2.二元运算的基本使用
            // 3.成员表达式的基本使用
            // 4.控制流语句的使用
            // 5.Lambda 表达式的使用
            // 6.组合表达式的基本使用
        }

        /// <summary>
        /// 表达式基本原素的创建
        /// </summary>
        public void Study3()
        {
            //// 1.参数与常量
            //ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "X");
            //ConstantExpression constantExpression = Expression.Constant(10, typeof(int));
            //ConstantExpression constantExpression1 = Expression.Constant(10);

            //// 2.变量
            //ParameterExpression parameterExpression1 = Expression.Variable(typeof(int), "Name");

            //// 在Block中使用变量
            //BlockExpression blockExpression = Expression.Block(
            //    new[] { parameterExpression1 },
            //    Expression.Assign(parameterExpression1, Expression.Constant(5)),
            //    parameterExpression1
            //    );

            //Console.WriteLine(blockExpression.ToString());
        }

        /// <summary>
        /// 运算表达式方法
        /// </summary>
        public void Study4()
        {
            // 算术运算
            BinaryExpression binaryExpression = Expression.And(Expression.Constant(2), Expression.Constant(3));

            // 比较运算

            // =
            BinaryExpression binaryExpression1 = Expression.Equal(binaryExpression, Expression.Constant(4));

            // !=
            BinaryExpression binaryExpression2 = Expression.NotEqual(binaryExpression1, Expression.Constant(5));

            // >
            BinaryExpression binaryExpression3 = Expression.GreaterThan(binaryExpression1, binaryExpression2);

            // <
            BinaryExpression binaryExpression4 = Expression.LessThan(binaryExpression3, Expression.Constant(6));

            // 逻辑运算符

            // &
            BinaryExpression binaryExpression5 = Expression.Add(binaryExpression1, binaryExpression2);

            // |
            BinaryExpression binaryExpression6 = Expression.Or(binaryExpression5, binaryExpression4);

            // &&
            BinaryExpression binaryExpression7 = Expression.AndAlso(binaryExpression6, binaryExpression5);

            // || 
            BinaryExpression binaryExpression8 = Expression.OrElse(binaryExpression6, binaryExpression7);

            // !
            UnaryExpression unaryExpression = Expression.Not(binaryExpression8);

        }

        /// <summary>
        /// 成员访问与方法调用
        /// </summary>
        public void Study5()
        {
            // 1.属性和字段的访问
            ParameterExpression param = Expression.Parameter(typeof(int), "X");
            Expression.Field(param, "_internalField");
            Expression.Property(param, "Length");
            // 静态成员的访问
            Expression.Property(null, typeof(DateTime), "Now");

            ParameterExpression personParam = Expression.Parameter(typeof(Persion), "P");

            // 2.方法的调用

            // p.SayHello("World");

            Expression.Call(
                personParam,
                typeof(Persion).GetMethod("SayHello"),
                Expression.Constant("World")
                );

            // string.Concat("Hello","","World");
            // 静态方法的调用
            Expression.Call(
                typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string), typeof(string) }),
                Expression.Constant("Hello"),
                Expression.Constant(""),
                Expression.Constant("World")
            );
        }

        /// <summary>
        /// 对象的创建与初始化
        /// </summary>
        public void Study6()
        {
            NewExpression newExpression = Expression.New(
                typeof(Persion).GetConstructor(new[] { typeof(string), typeof(int) }),
                Expression.Constant("Join"),
                        Expression.Constant(25)
                );

            Persion persion = Expression.Lambda<Func<Persion>>(newExpression).Compile().Invoke();
            persion.SayHello("你好");
        }

        /// <summary>
        /// Expression构建表达式树
        /// </summary>
        public void Study7()
        {
            Expression<Func<int, int>> squareExpression = x => x * x;
            Console.WriteLine(squareExpression); // 输出 x => (x * x)

            // 编译并执行表达式树
            Func<int, int> func = squareExpression.Compile();
            int v = func.Invoke(5);
            Console.WriteLine(v);

        }

        /// <summary>
        /// 动态调用任意方法
        /// </summary>
        public void Study8()
        {
            Persion persion = new Persion("嘻嘻", 6);
            ConstantExpression constantExpression = Expression.Constant(persion);
            System.Reflection.MethodInfo? methodInfo = persion.GetType().GetMethod("SayHello");
            ParameterExpression parameterExpression = Expression.Parameter(typeof(string), "str");
            MethodCallExpression methodCallExpression = Expression.Call(constantExpression, methodInfo, parameterExpression);
            Expression<Action<string>> expression = Expression.Lambda<Action<string>>(methodCallExpression, parameterExpression);
            expression.Compile().Invoke("你好");
        }
    }

    public class Persion
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public Persion(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public void SayHello(string str)
        {
            Console.WriteLine(string.Format("name{0},age{1},SayHello,{2}", Name, Age, str));
        }
    }
}

2.3 学习表达式的常见错误

变量的作用域在一个块内才有效,变量可以定义在作用域的外面。但是变量必须放在块内。

Lambda表达式的作用域对于 参数或者变量定义可以放在外面, 但是对于在body 内使用的参数或者变量,必须要在lambda 里面传入参数或者变量表达式对象。否则就会出现在此作用域内未定义的异常报错。

在lambda里面传入变量表达式实例参数。

csharp 复制代码
  ParameterExpression jExp = Expression.Variable(typeof(int), "j");

 Expression<Func<int, int>> expression = Expression.Lambda<Func<int, int>>(
     Expression.Multiply(jExp, jExp),  //  参数 j 在此作用域内可见
     jExp
     );

 Console.WriteLine(expression.Compile().Invoke(10));

因为这里委托传入一个参数,但有时候不需要传入参数,但是想要使用参数表达式,又必须在Lmbda 的主体作用域范围里面有效,此时必须要借助表达式语句块来实现。

csharp 复制代码
 Expression.Assign(jExp, Expression.Constant(5));
 BinaryExpression assign6 = Expression.Assign(jExp, Expression.Constant(6));
 BinaryExpression binaryExpression = Expression.Multiply(jExp, jExp);
 BinaryExpression binaryExpression1 = Expression.Assign(jExp, binaryExpression);
 BlockExpression blockExpression = Expression.Block(new[] { jExp }, assign6, binaryExpression, binaryExpression1, jExp);
 Expression<Func<int>> expression1 = Expression.Lambda<Func<int>>(blockExpression);
 Console.WriteLine(expression1.Compile().Invoke());

作用域这个对于初学者来说非常容易出错,这个点需要注意哦。

3. Expression和 委托的区别

表达式动态编译之后就是委托。

表达式描述可以根据代码逻辑动态构建代码结构,是动态的。

而委托是已经完成的,代表一个固定的代码片段。

4. Expression的使用场景

  • 依赖注入
    在依赖注入的框架的实现,是通过表达式来完成的,其中包括创建者,对注入的不同类型创建一个对象,然后为对象属性赋值,最后通过容器获取到这个对象,这里的实现过程通过表达式来实现的。
  • Linq多条件动态构建多条件查询 Where 里面就是一个委托,通过构建表达式分析生成 委托
  • ORM框架:Entity Framework,Dapper等 (这里暂时未遇到,等遇到再更新)
  • 对象属性映射
相关推荐
Dersun2 小时前
python学习进阶之异常和文件操作(三)
开发语言·python·学习·json
白菜上路2 小时前
C# Web API Mapster基本使用
前端·c#
LostXerxes2 小时前
封装与属性
c#
LostXerxes2 小时前
C#类与对象
c#
Tiger_shl2 小时前
【.Net技术栈梳理】01-核心框架与运行时(CLR)
开发语言·.net
Tiger_shl2 小时前
【.Net技术栈梳理】02-核心框架与运行时(GC管理)
开发语言·.net
阿里matlab建模师2 小时前
【直流电机鲁棒控制】matlab实现H无穷大控制的直流电机鲁棒控制研究
开发语言·数学建模·matlab·全国大学生数学建模竞赛·美赛·科研项目
MediaTea2 小时前
Python 第三方库:SymPy(符号计算工具)
开发语言·python·数学建模
他们都不看好你,偏偏你最不争气3 小时前
【iOS】UIViewController
开发语言·macos·ios·objective-c·cocoa