"IQueryable<T>"和"IEnumerable<T>"有什么区别?

477

1
同时,已经形成共识,这种类型的问题不再属于主题范围。 - Gert Arnold
12个回答

296
首先,IQueryable<T> 扩展了 IEnumerable<T> 接口,因此您可以像使用“纯” IEnumerable<T> 一样使用 IQueryable<T>IEnumerable<T> 只有一个 GetEnumerator() 方法,它返回一个 Enumerator<T>,您可以调用其 MoveNext() 方法来迭代遍历 T 序列。 IQueryable<T> 拥有而 IEnumerable<T> 没有 的是两个特定的属性——其中一个指向一个查询提供程序(例如 LINQ to SQL 提供程序),另一个指向一个查询表达式,表示将 IQueryable<T> 对象作为运行时可遍历的抽象语法树,并且能够被给定的查询提供程序理解(基本上,您不能将一个 LINQ to SQL 表达式传递给一个 LINQ to Entities 提供程序,否则会引发异常)。
该表达式可以仅是对象本身的常量表达式,也可以是由一组组合的查询运算符和操作数构成的更复杂的树。查询提供程序的 IQueryProvider.Execute()IQueryProvider.CreateQuery() 方法会传递一个 Expression,然后分别返回查询结果或另一个 IQueryable

11
使用AsQueryable有什么好处吗? - Jordan
9
唯一的好处是方便。AsQueryable() 方法将只是把一个可枚举对象强制转换为 IQueryable 接口类型并返回它,否则会用 ConstantExpression 对象包装它,并在返回的 EnumerableQuery 对象中引用它。 - Mark Cidade
4
相关问题:IEnumerable<T> 和 IQueryable<T> 的澄清? - Christopher Stevenson
2
值得一读的文章 - RasikaSam
3
@JenonD提供的链接已失效,这里是Wayback Machine的存档链接:https://web.archive.org/web/20160904162931/http://www.dotnet-tricks.com/Tutorial/linq/I8SY160612-IEnumerable-VS-IQueryable.html - majjam
下面两个答案不够清晰。对我来说最好的答案是:https://dev59.com/TnE85IYBdhLWcg3wOw_s#2876655 - Alex Vovchuk

271
这是一个很好的YouTube视频,展示了这些接口之间的区别,值得观看。
以下是对此的详细解释。
首先要记住的重要点是,IQueryable接口继承自IEnumerable,因此无论IEnumerable能做什么,IQueryable也可以做到。

enter image description here

有很多区别,但让我们讨论一个最大的区别。当您的集合使用LINQ或Entity Framework加载并且您想在集合上应用过滤器时,IEnumerable接口非常有用。
考虑下面这个简单的代码,它使用了带有Entity Framework的IEnumerable。它使用了一个Where过滤器来获取EmpId2的记录。
EmpEntities ent = new EmpEntities();
IEnumerable<Employee> emp = ent.Employees; 
IEnumerable<Employee> temp = emp.Where(x => x.Empid == 2).ToList<Employee>();

这里的筛选器在客户端执行,其中包含 IEnumerable 代码。换句话说,所有数据都从数据库获取,然后在客户端扫描并获取具有 EmpId2 的记录。

enter image description here

但现在看下面的代码,我们已经将IEnumerable更改为IQueryable。它在服务器端创建一个SQL查询,并仅将必要的数据发送到客户端。
EmpEntities ent = new EmpEntities();
IQueryable<Employee> emp = ent.Employees;
IQueryable<Employee> temp =  emp.Where(x => x.Empid == 2).ToList<Employee>();

enter image description here

所以,IQueryableIEnumerable之间的区别在于过滤逻辑执行的位置。一个在客户端执行,另一个在数据库中执行。
因此,如果您只使用内存数据集合,则IEnumerable是一个不错的选择,但如果您想查询连接到数据库的数据集合,则IQueryable是一个更好的选择,因为它减少了网络流量并利用了SQL语言的优势。

1
非常感谢您精彩的解释。我明白为什么IQueryable是处理“内存不足”数据的更好选择,但我仍然不理解为什么IEnumerable对于“内存中”的数据更好?我仍然认为获取筛选后的数据比获取数据并应用筛选器更好。 - Imad
当数据“在内存中”时,时间并不重要。数据已经在内存中了,筛选的确切时间并不重要。 - Roni Axelrad
例如,通过将实体DateTimeOffset转换为DateTime来投影数据无法使用IQueryable或EntityFunctions进行。最好的方法是对实体对象进行AsEnumerable()操作,然后选择包含DateTime的视图模型。此过程使用内存中的函数。@Imad - Jeff

230
主要区别在于针对 IQueryable<T> 的 LINQ 运算符接收的是表达式树而不是委托,这意味着它所接收的自定义查询逻辑(例如谓词或值选择器)以表达式树的形式而不是方法委托的形式表示。
  • IEnumerable<T> 适用于在内存中迭代的序列处理,并且
  • IQueryable<T> 允许处理外部数据源,例如远程数据源,如数据库或 Web 服务。

查询执行:

  • 如果查询执行将在进程内进行,通常只需要代码(作为代码)来执行查询的每个部分。
  • 如果查询执行将在进程外部进行,则查询的逻辑必须以数据的形式表示,以便 LINQ 提供程序可以将其转换为适合外部内存执行的适当形式,无论是 LDAP 查询、SQL 还是其他形式。

更多内容请参见:

  • 响应式编程(Reactive Programming)对于 .NET 和 C# 开发者的介绍 - 详解 IEnumerable, IQueryable, IObservable, 以及 IQbservable
  • 2008 年,来自 Bart De Smet 的 "年度最有趣接口……IQUERYABLE<T>"。
  • http://www.codeproject.com/KB/cs/646361/WhatHowWhere.jpg


    18
    我喜欢提到内存不足操作,它阐明了使用中的实际差异。 - Ishmael Smyrnow
    2
    与IEnumerable<T>的Where方法接受委托作为参数不同,IQueryable<T>将接受Expression<TDelegate>参数。我们不是将代码传递给IQueryable<T>,而是传递表达式树(代码作为数据)供LINQ提供程序分析。 - Ilya Serbis
    你的图片链接出了些问题,不知道为什么无法在帖子正文中渲染。链接地址是 http://www.codeproject.com/KB/cs/646361/WhatHowWhere.jpg。 - Judy007
    @joey,然而我在这个答案中可以很好地看到那张图片:http://i.stack.imgur.com/2DAqv.jpg - VonC
    这个答案很好,直到那个代表并发、拉取与推送、IObservable<T>的嘈杂图表出现,只有大约5%的图表涉及到IEnumerable<T>IQueryable<T>;同样的情况也适用于“响应式编程”链接,其中IEnumerable<T> / IQueryable<T>之间的区别仅作为介绍响应式扩展。此外,我建议包括一个指向Bart de Smet的今年最有趣的接口...IQueryable<T>的链接。 - Zev Spitz
    @ZevSpitz 感谢您的反馈。我已经在答案中包含了您的链接。如果您能够提供一个更相关的图片,我将非常欢迎您的编辑。 - VonC

    63

    IEnumerable: IEnumerable适用于处理内存中的集合(或本地查询)。 IEnumerable不会在项之间移动,它是一个只能向前移动的集合。

    IQueryable: IQueryable适用于远程数据源,如数据库或Web服务(或远程查询)。 IQueryable是一个非常强大的功能,可以实现各种有趣的延迟执行场景(如分页和基于组合的查询)。

    因此,如果您只需要迭代内存中的集合,请使用IEnumerable;如果您需要对集合进行任何操作(如Dataset和其他数据源),请使用IQueryable。


    3
    IEnumerable使用延迟执行吗? - Ian Warburton
    3
    延迟执行在IEnumerable和IQueryable中都可用。更多有趣的信息请参见此处:https://dev59.com/TnE85IYBdhLWcg3wOw_s - Ignacio Hagopian

    24

    简单来说,另一个主要区别是 IEnumerable 在服务器端执行 select 查询,将数据加载到客户端内存中,然后过滤数据,而 IQueryable 则在服务器端执行带有所有筛选器的 select 查询。


    19

    在现实生活中,如果您正在使用像LINQ-to-SQL这样的ORM:

    • 如果创建了一个IQueryable,则查询可能会转换为SQL并在数据库服务器上运行。
    • 如果创建了一个IEnumerable,则在运行查询之前所有行都将作为对象加载到内存中。

    在这两种情况下,如果您没有调用ToList()ToArray(),则每次使用查询时都会执行查询。例如,如果您有一个IQueryable<T>并从中填充了4个列表框,则查询将针对数据库运行4次。

    另外,如果您扩展查询:

    q.Where(x.name = "a").ToList()
    

    如果使用IQueryable,生成的 SQL 将包含“where name = 'a'”语句,但如果使用IEnumerable,将会从数据库中拉取更多记录,然后由 .NET 执行 x.name = 'a' 的检查。


    18
    我们使用 IEnumerableIQueryable 来操作从数据库检索出来的数据。 IQueryable 继承自 IEnumerable,因此 IQueryable 包含了所有 IEnumerable 的特性。 IQueryableIEnumerable 的主要区别在于,IQueryable 使用过滤器执行查询,而 IEnumerable 先执行查询,然后根据条件过滤数据。

    以下是更详细的区别:

    IEnumerable

    1. IEnumerable 存在于 System.Collections 命名空间中
    2. IEnumerable 在服务器端执行选择查询,在客户端加载数据到内存中,然后过滤数据
    3. IEnumerable 适用于从内存集合(如 List、Array)查询数据
    4. IEnumerable 对于 LINQ to Object 和 LINQ to XML 查询非常有益

    IQueryable

    1. IQueryable 存在于 System.Linq 命名空间中
    2. IQueryable 在服务器端执行带有所有过滤器的“select query”
    3. IQueryable 适用于从外部内存(如远程数据库、服务)集合查询数据
    4. IQueryable 对于 LINQ to SQL 查询非常有益

    通常情况下,IEnumerable 用于处理内存中的集合,而 IQueryable 则用于操纵集合。


    16

    以下提到的小测试可能有助于您理解 IQueryable<T>IEnumerable<T> 之间差异的一个方面。我从帖子中复制了这个答案,在那里我试图对别人的帖子进行更正。

    我在数据库中创建了以下结构(DDL脚本):

    CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Salary] [int] NOT NULL)
    

    这是记录插入脚本(DML脚本):

    INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(1, 20)
    INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(2, 30)
    INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(3, 40)
    INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(4, 50)
    INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(5, 60)
    GO
    

    现在我的目标是从数据库的表中获取前2条记录。我在Visual Studio (VS)中创建了一个新的C#控制台应用程序,然后添加了一个指向数据库中表的ADO.NET实体数据模型XML (EDMX)项。现在我可以开始编写LINQ查询。

    情况I: IQueryable路由的代码

    using (var efContext = new EfTestEntities())
    {
        IQueryable<int> employees = from e in efContext.Employees  select e.Salary;
        employees = employees.Take(2);
    
        foreach (var item in employees)
        {
            Console.WriteLine(item);
        }
    }
    

    在运行此程序之前,我已经在SQL Server实例上启动了一个SQL查询分析器会话。以下是执行摘要:

    1. 触发的查询总数:1

    2. 查询文本:

      SELECT TOP (2) [c].[Salary] AS [Salary] FROM [dbo].[Employee] AS [c]
      

    我们可以看到,IQueryable 足够智能,能够在数据库服务器端自行应用 Top (2) 子句。因此,它仅通过网络带来了5条记录中的2条。客户端不再需要进行内存过滤。

    情况二: IEnumerable 路由的代码

    using (var efContext = new EfTestEntities())
    {
        IEnumerable<int> employees = from e in efContext.Employees  select e.Salary;
        employees = employees.Take(2);
    
        foreach (var item in employees)
        {
            Console.WriteLine(item);
        }
    }
    

    这个案例的执行摘要:

    1. 发出的查询总数: 1

    2. 在 SQL profiler 中捕获的查询文本:

      SELECT [Extent1].[Salary] AS [Salary] FROM [dbo].[Employee] AS [Extent1]
      

    需要注意的是,IEnumerable 会将 Salary 表中的所有 5 条记录带回客户端,然后在客户端进行内存过滤以获取前两条记录。因此,无谓地传输了更多数据(在这种情况下,额外的 3 条记录),并消耗掉了带宽。


    11

    IEnumerable指的是集合,而IQueryable只是一个查询,在表达式树内生成。我们将运行此查询以从数据库中获取数据。


    8

    以下是我在类似主题的帖子上写的内容(关于此主题)。 (不,我通常不引用自己,但这些是非常好的文章。)

    "这篇文章很有帮助:LINQ-to-SQL中的IQueryable vs IEnumerable

    引用该文章,“根据MSDN文档,对IQueryable调用操作是通过构建内部表达式树来实现的。 “这些扩展IQueryable(Of T)的方法不直接执行任何查询。相反,它们的功能是构建一个表达式对象,即表示累积查询的表达式树。”'

    表达式树是C#和.NET平台中非常重要的构造。 (它们在一般情况下也很重要,但C#使它们非常有用。)为了更好地理解区别,我建议阅读有关表达式和语句之间差异的官方C#5.0规范的差异。对于涉及lambda演算的高级理论概念,表达式使支持方法作为一级对象成为可能。 IQueryable和IEnumerable之间的区别集中在这一点上。IQueryable构建表达式树,而IEnumerable不会,至少对于我们这些不在Microsoft秘密实验室工作的人来说。

    这是另一篇非常有用的文章,详细介绍了从推送vs.拉动的角度的差异。(通过“推送”与“拉动”,我指的是数据流的方向。用于.NET和C#的反应式编程技术

    这是一篇非常好的文章,详细介绍了语句lambda和表达式lambda之间的差异,并讨论了表达式树的概念:重温C#委托,表达式树和lambda语句vs.lambda表达式。。”


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接