IQueryable,ICollection,IList& IDictionary接口

时间:2010-12-15 22:06:02

标签: c# .net performance list collections

我试图理解IQueryable,ICollection,IList& IDictionary接口 这对于迭代,索引,查询等基本操作来说更快。

像Collection,List,Dictionary等类这样的类可以很好地启动这些接口,什么时候应该使用这些类。使用这些类比其他类的基本优势。

我尝试阅读其他类似问题的帖子,但没有回答我的完整问题。 谢谢你的帮助。

3 个答案:

答案 0 :(得分:102)

所有这些接口都继承自 IEnumerable ,您应该确保理解。该接口基本上允许您在foreach语句中使用该类(在C#中)。

  • ICollection 是您列出的最基本的界面。它是一个可枚举的接口,支持Count,就是它。
  • IList 是ICollection的所有内容,但它也支持添加和删除项目,按索引检索项目等。它是“对象列表”最常用的界面,这是模糊的我知道。
  • IQueryable 是一个支持LINQ的可枚举接口。您始终可以从IList创建IQueryable并使用LINQ to Objects,但您也可以在LINQ to SQL和LINQ to Entities中找到用于延迟执行SQL语句的IQueryable。
  • IDictionary 在某种意义上是一种不同的动物,它是唯一键到值的映射。它也是可枚举的,你可以枚举键/值对,但除此之外,它的目的与你列出的其他目的不同。

每个MSDN文档都很不错,所以我会从那里开始理解你的理解。

答案 1 :(得分:3)

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

IEnumerable和IQueryable之间的差异 IEnumerable : - 运行返回IEnumerable类型的查询时。 IEnumerable将首先执行第一个查询,然后执行为该查询编写的子查询。

示例: - 如果您想从特定国家/地区获取前10个人口记录,那么我们将在LinQ中使用的查询

IEnumerable _Population =来自印度的s选择s.population; // First Query _Population = _Population.take(10); //第二次查询

现在,如果我们执行此查询,首先将执行First Query,IEnumerable将从sql server获取所有填充的记录,然后它将数据存储在In Process内存中,然后执行前10个填充next Query。(在sql server上执行两次)。

如果我们使用IQueryable执行相同的查询。

IQueryable _Population =来自印度的s选择s.population; // First Query _Population = _Population.take(10); //第二次查询

在这个IQueryable中,它将同时执行两个查询,因为它将获得单个查询中前10名的填充,而不是获取数据并再次过滤前10个。(一次执行sql server)。

IQueryable和IEnumerable的结论

  1. 如果您正在针对数据库中的数据编写查询,那么 使用IQueryable。
  2. 如果您正在查询进程数据,那么 使用IEnumerable。 IEnumerable有一个方法GetEnumerator(),Current()和MoveNext()。

答案 2 :(得分:3)

我注意到上面@ gunny229的答案中有几个问题,这就是我写这篇文章的原因。我也在他的帖子的评论区域中提到了这些问题。为了纠正这个帖子,我不得不重写几乎所有帖子,所以我想创建自己的帖子。我并不打算完整地讨论OP的问题,但是我想指出使用LINQ to SQL 时IQueryable和IEnumerable之间的差异。

我在DB(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表中获取前2条记录。所以,我在我的控制台应用程序中添加了一个ADO.NET实体数据模型项,指向我数据库中的Employee表并开始编写LINQ查询。

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 Query profiler会话,这是执行摘要:

  1. 触发的查询总数: 1
  2. 查询文字:SELECT TOP (2) [c].[Salary] AS [Salary] FROM [dbo].[Employee] AS [c]
  3. 只是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事件探查器中捕获的文本:SELECT [Extent1].[Salary] AS [Salary] FROM [dbo].[Employee] AS [Extent1]
    3. 现在问题IEnumerable带来了Salary表中存在的所有5条记录,然后在客户端计算机上执行了内存过滤以获得前2条记录。因此,更多的数据(在这种情况下为3个额外的记录)不必要地通过线路传输。