C#表达式树(Expression Trees)的使用

 更新时间:2025年06月11日 08:26:07   作者:ghost143  
本文主要介绍了C#表达式树(Expression Trees)的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

什么是表达式树?

  • 表达式树是C#中一种数据结构,用于以树状方式表示代码中的表达式,每个节点代表一个操作(如算术运算、方法调用等)。
  • 它们允许你将代码本身视为数据结构,能够在运行时动态地分析、修改和执行代码。
  • 表达式树最初是在 .NET 3.5 中引入的,主要用于支持 LINQ(语言集成查询)。

 核心概念

1.表达式树的构建

  • 表达式树的核心类型位于 System.Linq.Expressions 命名空间。
  • 可以手动构建表达式树,也可以通过Lambda表达式隐式构建。
using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        // 创建一个简单的表达式:x => x + 1
        ParameterExpression param = Expression.Parameter(typeof(int), "x");
        BinaryExpression body = Expression.Add(param, Expression.Constant(1));
        Expression<Func<int, int>> expression = Expression.Lambda<Func<int, int>>(body, param);

        // 编译并执行表达式树
        Func<int, int> compiledExpression = expression.Compile();
        int result = compiledExpression(5);

        Console.WriteLine($"Result: {result}"); // 输出:Result: 6
    }
}

 在这个示例中,我们创建了一个简单的表达式树表示 x => x + 1,并将其编译成可执行代码。

2. 表达式树与Lambda表达式 

Lambda表达式可以被编译为委托,也可以被表达式树捕获:

Expression<Func<int, int>> square = x => x * x;

此时,square不是一个委托,而是一棵描述 x * x 计算过程的树,可用于分析和转换。

3.解析和访问表达式树

表达式树可以遍历并分析其结构:

void PrintExpression(Expression exp, int level = 0)
{
    Console.WriteLine(new string(' ', level * 2) + exp.NodeType + " - " + exp.Type);
    if (exp is BinaryExpression bin)
    {
        PrintExpression(bin.Left, level + 1);
        PrintExpression(bin.Right, level + 1);
    }
    else if (exp is ParameterExpression param)
    {
        Console.WriteLine(new string(' ', (level+1) * 2) + "Parameter: " + param.Name);
    }
}

4.动态条件查询

我们有一个 Product 类和一个产品列表。我们希望根据产品的价格动态过滤产品。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

class Program
{
    static void Main()
    {
        // 创建产品列表
        List<Product> products = new List<Product>
        {
            new Product { Name = "Laptop", Price = 1000m },
            new Product { Name = "Smartphone", Price = 500m },
            new Product { Name = "Tablet", Price = 300m }
        };

        // 动态创建表达式树来过滤价格大于 400 的产品
        Func<Product, bool> filter = CreatePriceFilter(400m);

        // 使用生成的过滤器查询产品
        var filteredProducts = products.Where(filter).ToList();

        foreach (var product in filteredProducts)
        {
            Console.WriteLine($"Product: {product.Name}, Price: {product.Price}");
        }
    }

    static Func<Product, bool> CreatePriceFilter(decimal minPrice)
    {
        // 创建参数表达式
        ParameterExpression param = Expression.Parameter(typeof(Product), "product");

        // 创建访问属性表达式
        MemberExpression priceProperty = Expression.Property(param, "Price");

        // 创建常量表达式
        ConstantExpression constant = Expression.Constant(minPrice);

        // 创建大于运算符表达式
        BinaryExpression comparison = Expression.GreaterThan(priceProperty, constant);

        // 创建 lambda 表达式
        Expression<Func<Product, bool>> lambda = Expression.Lambda<Func<Product, bool>>(comparison, param);

        // 编译表达式树为可执行代码
        return lambda.Compile();
    }
}

1.设置产品列表:

  •  我们先定义一个简单的 Product 类和一个包含几个产品的列表。

2.创建表达式树:

  • 参数表达式:ParameterExpression param = Expression.Parameter(typeof(Product), "product"); 创建一个参数,表示传递给过滤器的 Product 对象。
  • 属性访问表达式:MemberExpression priceProperty = Expression.Property(param, "Price"); 访问传递对象的 Price 属性。
  • 常量表达式:ConstantExpression constant = Expression.Constant(minPrice); 定义过滤条件中的常量值。
  • 比较表达式:BinaryExpression comparison = Expression.GreaterThan(priceProperty, constant); 创建一个比较表达式,检查 Price 是否大于 minPrice。
  • lambda 表达式:将上述表达式组合成一个完整的 lambda 表达式,并编译成可执行代码。

3.应用表达式:

  • 使用 Where 方法将生成的过滤器应用于产品列表,并输出结果。

表达式树的优势

1.动态构建查询

  • 表达式树允许你在运行时构建和修改查询逻辑。这在需要根据用户输入或其他动态数据生成不同查询条件时特别有用。

2.LINQ 提供程序支持:

  • 表达式树是 LINQ 提供程序(如 LINQ to SQL、Entity Framework)的基础,它们将表达式树解析为底层数据源(如数据库、XML)的查询语言。这意味着你可以用相同的代码生成运行在不同数据源上的查询。

3.性能优化

  • 在某些情况下,表达式树可以被编译和缓存,提高重复执行相同逻辑的性能。

4.元数据处理

  • 表达式树提供对表达式结构的访问,这使得分析和处理代码元数据成为可能。这对于开发动态应用程序或框架尤其有用。

5.代码转换和重写

  • 可以编写代码来遍历和修改表达式树,用于实现代码转换或重写。这对于构建复杂查询或分析工具有很大帮助。

适用场景 

  • 动态条件查询:当应用需要支持用户定义的动态过滤条件时,表达式树可以灵活地构建这些条件。
  • 跨平台查询:在 LINQ to SQL 或 Entity Framework 中,表达式树可被翻译成 SQL 查询,在数据库执行。
  • 规则引擎和DSL(领域特定语言):在这些场景中,表达式树可以用于解析和执行用户定义的规则或查询 

代码复杂性的权衡 

  • 虽然表达式树代码在某些情况下显得复杂,但其提供的灵活性和功能在复杂应用中是非常关键的。
  • 如果只是简单的筛选条件,直接使用 Lambda 表达式或 LINQ 查询语法更为直接和清晰。

示例1:

假设我们有一个产品列表,用户可以动态选择多个条件进行过滤,比如根据名称、价格范围或库存状态等进行筛选。我们需要在运行时根据用户输入组合这些条件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool InStock { get; set; }
}

class Program
{
    static void Main()
    {
        var products = new List<Product>
        {
            new Product { Name = "Laptop", Price = 1000, InStock = true },
            new Product { Name = "Smartphone", Price = 500, InStock = true },
            new Product { Name = "Tablet", Price = 300, InStock = false }
        };

        // 用户可以选择动态条件
        string searchName = "Laptop";
        decimal? minPrice = 400;
        decimal? maxPrice = null;
        bool? inStock = true;

        // 创建动态查询表达式
        var filter = CreateDynamicFilter<Product>(searchName, minPrice, maxPrice, inStock);

        // 使用生成的过滤器查询产品
        var filteredProducts = products.AsQueryable().Where(filter).ToList();

        foreach (var product in filteredProducts)
        {
            Console.WriteLine($"Product: {product.Name}, Price: {product.Price}, InStock: {product.InStock}");
        }
    }

    static Expression<Func<T, bool>> CreateDynamicFilter<T>(string name, decimal? minPrice, decimal? maxPrice, bool? inStock)
    {
        // 参数表达式
        var parameter = Expression.Parameter(typeof(T), "product");
        Expression expression = Expression.Constant(true); // 初始谓词为 true

        // 根据 name 动态创建条件
        if (!string.IsNullOrEmpty(name))
        {
            var nameProperty = Expression.Property(parameter, "Name");
            var nameValue = Expression.Constant(name);
            var nameExpression = Expression.Equal(nameProperty, nameValue);
            expression = Expression.AndAlso(expression, nameExpression);
        }

        // 根据 minPrice 创建条件
        if (minPrice.HasValue)
        {
            var priceProperty = Expression.Property(parameter, "Price");
            var minPriceValue = Expression.Constant(minPrice.Value);
            var minPriceExpression = Expression.GreaterThanOrEqual(priceProperty, minPriceValue);
            expression = Expression.AndAlso(expression, minPriceExpression);
        }

        // 根据 maxPrice 创建条件
        if (maxPrice.HasValue)
        {
            var priceProperty = Expression.Property(parameter, "Price");
            var maxPriceValue = Expression.Constant(maxPrice.Value);
            var maxPriceExpression = Expression.LessThanOrEqual(priceProperty, maxPriceValue);
            expression = Expression.AndAlso(expression, maxPriceExpression);
        }

        // 根据 inStock 创建条件
        if (inStock.HasValue)
        {
            var stockProperty = Expression.Property(parameter, "InStock");
            var stockValue = Expression.Constant(inStock.Value);
            var stockExpression = Expression.Equal(stockProperty, stockValue);
            expression = Expression.AndAlso(expression, stockExpression);
        }

        // 创建 Lambda 表达式
        return Expression.Lambda<Func<T, bool>>(expression, parameter);
    }
}

示例2:

针对上文中只针对价格做筛选的示例,那么我们的筛选过程完全可以简化成如下表达式

var filteredProducts = products.Where(p => p.Price > 400).ToList();

总结来说,是否使用表达式树取决于你的具体需求和应用场景。在需要动态处理和复杂逻辑的情况下,表达式树提供了强大的工具支持,而在简单场景下,直接使用 Lambda 表达式或常规方法更为合适。希望这能帮助你理解表达式树的适用场景和优势!

到此这篇关于C#表达式树(Expression Trees)的使用的文章就介绍到这了,更多相关C#表达式树内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于C#实现哈夫曼树算法

    基于C#实现哈夫曼树算法

    哈夫曼树又称最优二叉树,也就是带权路径最短的树,对于哈夫曼树,我想大家对它是非常的熟悉,使用下面我们就来学习一下如何通过C#实现哈夫曼树算法吧
    2023-11-11
  • C#中System.Text.Json匿名对象反序列化

    C#中System.Text.Json匿名对象反序列化

    这篇文章主要介绍了System.Text.Json匿名对象反序列化,下文代码基于. NET 6,为了代码整洁,实际配置了PropertyNameCaseInsensitive = true,本文结合实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • C#中数组、ArrayList、List、Dictionary的用法与区别浅析(存取数据)

    C#中数组、ArrayList、List、Dictionary的用法与区别浅析(存取数据)

    在工作中经常遇到C#数组、ArrayList、List、Dictionary存取数据,但是该选择哪种类型进行存储数据呢?很迷茫,今天小编抽空给大家整理下这方面的内容,需要的朋友参考下吧
    2017-02-02
  • C#实现汽车租赁系统项目

    C#实现汽车租赁系统项目

    这篇文章主要为大家详细介绍了C#实现汽车租赁系统项目,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • 利用lambda表达式树优化反射详解

    利用lambda表达式树优化反射详解

    这篇文章主要给大家介绍了关于如何利用lambda表达式树优化反射的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-12-12
  • C#实现简单的Login窗口实例

    C#实现简单的Login窗口实例

    这篇文章主要介绍了C#实现简单的Login窗口,实例分析了C#显示及关闭登陆Login窗口的技巧,非常具有实用价值,需要的朋友可以参考下
    2015-08-08
  • C#实现计算年龄的简单方法汇总

    C#实现计算年龄的简单方法汇总

    本文给大家分享的是C#代码实现的简单实用的给出用户的出生日期,计算出用户的年龄的代码,另外附上其他网友的方法,算是对计算年龄的一次小结,希望大家能够喜欢。
    2015-05-05
  • 换个方式使用C#开发微信小程序的过程

    换个方式使用C#开发微信小程序的过程

    这篇文章主要介绍了换个方式使用C#开发微信小程序的过程,演示使用C#写的LiveCharts,点击按钮动态生成一些数据,感兴趣的朋友跟随小编一起看看吧
    2025-05-05
  • C#中图片.BYTE[]和base64string的转换方法

    C#中图片.BYTE[]和base64string的转换方法

    下面小编就为大家带来一篇C#中图片.BYTE[]和base64string的转换方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • C# Invoke,begininvoke的用法详解

    C# Invoke,begininvoke的用法详解

    这篇文章主要介绍了C# Invoke,begininvoke的用法详解,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下
    2021-01-01

最新评论