IEnumerable<T> vs IQueryable<T>: 在 C# 中如何选择?全面解析

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

IEnumerable<T> vs IQueryable<T>:决定你应用程序命运的决斗

\n\n

你是否曾想过,为什么你的数据库查询会变慢,尽管数据量少得可怜?或者为什么添加一行代码后,应用程序就变得像乌龟一样慢?很可能,你遇到了经典的难题:IEnumerable<T> 还是 IQueryable<T>?这不仅仅是 C# 和 LINQ 世界中的两个接口。这是两个不同的世界,两种处理数据的方法。一个用于本地集合,另一个用于远程数据源,例如数据库。在本文中,我们不仅会列出区别,还会通过实际示例进行剖析,让你再也不会陷入性能陷阱。

\n\n

谁是谁:基本概念

\n\n

让我们从基础开始。IEnumerable<T> 是一个表示内存中元素序列的接口。它非常适合处理集合,例如 List<T>、Array 或 Dictionary。当你使用 IEnumerable 时,所有操作(过滤、排序)都在客户端执行,即在你的应用程序中。

\n\n

IQueryable<T> 是一个更高级的接口,专为处理外部数据源(数据库、Web 服务)而设计。它不存储数据,而是构建一个表达式树(Expression Tree)。这使得 LINQ 提供程序(例如 Entity Framework)能够将你的 C# 代码转换为高效的 SQL 查询,并在服务器端执行。

\n\n

需要记住的关键短语是:IEnumerable 在内存中工作,IQueryable 在数据源端工作。

\n\n

主要区别:逻辑在哪里以及何时执行

\n\n

想象一下,你需要过滤一个包含 10,000 件商品的列表,找出价格超过 1000 元的商品,并按价格排序。

\n\n

示例 1:IEnumerable(内存中过滤)

\n
// 假设 products 是 List<Product> (IEnumerable)\nvar expensiveProducts = products\n    .Where(p => p.Price > 1000)\n    .OrderBy(p => p.Price)\n    .ToList();\n
\n

这里发生了什么?首先,所有 10,000 件商品都被加载到内存中。然后,C# 在你的应用程序中应用 Where 过滤器和 OrderBy 排序。如果你有 10,000 条记录,这还不算严重。但如果有 1000 万条,应用程序就会崩溃或卡死。

\n\n

示例 2:IQueryable(数据库服务器端过滤)

\n
// 假设 dbContext.Products 是 DbSet<Product> (IQueryable)\nvar expensiveProducts = dbContext.Products\n    .Where(p => p.Price > 1000)\n    .OrderBy(p => p.Price)\n    .ToList();\n
\n

这里的魔法在于:IQueryable 不会立即加载数据。相反,它会构建一个 SQL 查询:SELECT * FROM Products WHERE Price > 1000 ORDER BY Price。这个查询被发送到数据库,数据库只返回过滤和排序后的数据。你在内存中只得到你需要的内容。资源节省是巨大的。

\n\n

实践示例:ToList() 陷阱

\n\n

初学者最常见的错误之一是过早调用 ToList() 或 ToArray()。这会将 IQueryable 转换为 IEnumerable。

\n\n

糟糕的代码(扼杀性能):

\n
var allProducts = dbContext.Products.ToList(); // 将所有数据加载到内存中!\nvar filteredProducts = allProducts.Where(p => p.Price > 1000).ToList();\n
\n

在这个例子中,我们首先将整个 Products 表加载到内存中,然后再进行过滤。这相当于把整个仓库卸到停车场,然后在一堆货物中寻找需要的箱子。

\n\n

好的代码(最优):

博客

书籍推荐