IQueryable,ICollection,IList和IDictionary接口的区别

59

我试图理解IQueryable、ICollection、IList和IDictionary接口之间的差异,哪个更适合基本操作,如迭代、索引、查询等。

在使用这些接口时,像Collection、List、Dictionary等类应该是很好的选择。使用这些类的基本优势。

我尝试阅读其他类似问题的帖子,但没有一个完整地回答了我的问题。感谢您的帮助。

3个回答

123
所有这些接口都继承自IEnumerable,你应该确保你理解了这个接口。这个接口基本上允许您在C#的foreach语句中使用类。
  • ICollection 是您列出的接口中最基本的接口。它是一个可枚举的接口,支持计数功能。
  • IList 包括 ICollection 的所有功能,并且还支持添加和删除项目,通过索引检索项目等。它是“对象列表”最常用的接口,虽然这个说法有点模糊。
  • IQueryable 是一种支持LINQ的可枚举接口。您可以始终从IList创建一个IQueryable并使用LINQ to Objects,但您还会在LINQ to SQL和LINQ to Entities中使用IQueryable来延迟执行SQL语句。
  • IDictionary 是一种不同类型的接口,它是将唯一键映射到值的集合。虽然您可以枚举键/值对,但它提供的功能与其他列出的接口有所不同。
每个接口都有较好的MSDN文档,因此您可以从那里开始了解它们。

+1 针对这些接口的详细解释,但是你忽略了我其他的问题。 - Praneeth
17
关于接口的通用版本,ICollection<T> 支持 Add/Remove/Clear/Contains/CopyTo 操作,具体信息可参考 http://msdn.microsoft.com/en-us/library/92t2ye13.aspx。 - Joshua Rodgers

11

我注意到 gunny229 的答案 中存在一些问题。我已经在他的帖子评论区中提到了这些问题。后来,我想写一篇更详细的文章来连接一些遗漏的内容。

免责声明: 我并不打算完全满足 OP 的问题,但我想指出在使用 LINQ to SQL 时 IQueryableIEnumerable 的区别。

我在数据库中创建了以下结构(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

我现在的目标是从数据库的Employee表中获取前两条记录。我在Visual Studio(VS)中创建了一个新的C#控制台应用程序,然后添加了指向数据库中Employee表的ADO.NET Entity Data Model 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. Total number of queries fired: 1

  2. Query text:

    SELECT TOP (2) [c].[Salary] AS [Salary] FROM [dbo].[Employee] AS [c]
    
我们可以看到,IQueryable 足够智能,能够在数据库服务器端自行应用 Top (2) 子句。 因此,它仅通过网络传输其中的 2 条记录,而不需要在客户端进行任何内存过滤。 案例 II: 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. Total number of queries fired: 1

  2. Query text captured in SQL profiler:

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

需要注意的是,IEnumerableSalary 表中获取了所有5条记录,然后在客户端执行内存过滤以获得前2条记录。因此,更多的数据(在此情况下为3条额外记录)被传输到网络上,并且不必要地占用了带宽。


谢谢,但我不认为@gunny229的回答有任何误导性。是的,他使用了“查询”这个词,但这并不是在数据库的上下文中。这只是另一个计算,以获得前两个结果。至少这就是我阅读时发现的。干杯。 - Ε Г И І И О

3

IQueryable、IList、IDictionary、ICollection都继承自IEnumerable接口。所有类型集合的接口都将继承IEnumerable接口。

IEnumerable和IQueryable之间的区别 IEnumerable:当您运行返回IEnumerable类型的查询时,IEnumerable会先执行第一个查询,然后执行为该查询编写的子查询。

示例:如果您想从特定国家获取前10个人口记录,则在LINQ中使用的查询如下:

IEnumerable<LongInt> _Population=from s in India select s.population;//First Query
_Population=_Population.take(10);//Second Query

现在,如果我们执行这个查询,首先会执行第一个查询,IEnumerable将从SQL服务器获取所有人口记录,然后将数据存储在进程内存中,然后在下一个查询中执行前10个人口。(在SQL服务器上进行两次执行)。
如果我们使用IQueryable执行相同的查询。
IQueryable<LongInt>  _Population=from s in India select s.population;//First Query
_Population=_Population.take(10);//Second Query

在这个IQueryable中,它将同时执行两个查询,意味着它会在单个查询中获取前10个人口而不是再次获取数据并过滤前10个人口。(在SQL服务器上一次性执行)。
IQueryable和IEnumerable的结论:
1. 如果您正在针对数据库中的数据编写查询,请使用IQueryable。 2. 如果您正在针对进程内数据进行查询,请使用IEnumerable。IEnumerable具有GetEnumerator(),Current()和MoveNext()方法。

真的很高兴知道它们之间的区别。 (y) - Wasim Bajwa
1
扩展方法Take定义在IEnumerable<T>上,我验证了在IEnumerableIQueryable两种情况下只有1个查询被触发。只是IEnumerable进行内存过滤,这会将不需要的数据带到网络上。 - RBT

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