C# 中IEnumerable与IQuerable的区别

目的

详细理清IEnumerator、IEnumerable、IQuerable三个接口之间的联系与区别

继承关系:IEnumerator->IEnumerable->IQuerable

IEnumerator:枚举器

包含了枚举器含有的方法,谁实现了IEnuemerator接口中的方法,就可以自定义成为一个枚举器。

 public interface IEnumerator
    {
        [__DynamicallyInvokable]
        object Current
        {
            [__DynamicallyInvokable]
            get;
        }

        [__DynamicallyInvokable]
        bool MoveNext();

        [__DynamicallyInvokable]
        void Reset();
    }

实现了IEnumerator接口,就可以获得Current、MoveNext、以及Reset方法,可以实现迭代遍历等操作。
1.Current 返回序列中当前位置项的属性
2.MoveNext 把枚举器位置前进到集合的下一项中
3.Reset 把位置重置为原始状态的方法

public interface IEnumerable
    {
        [DispId(-4)]
        [__DynamicallyInvokable]
        IEnumerator GetEnumerator();
    }

在这里插入图片描述

IEnumerator就是一个枚举器,包含的方法可以遍历元素。IEnumerable、IQuerable接口中都含有可以获取IEnumerator这个枚举器的GetEnumerator()方法,通过实现GetEnumerator方法来返回对象的枚举器。

IEnumerable接口:获取IEnumerator枚举器

IEnumerable是.NET Framework中的一个基础接口,用于遍历集合中的元素。它提供了一种通用的数据遍历方法,不依赖于数据的具体类型或结构。实现了IEnumerable接口的类或集合可以使用foreach语句进行遍历。枚举器提供了对集合中元素的逐个访问,以此来实现对集合的迭代。 使用 IEnumerable 接口可以使你的集合类通过 foreach 循环来遍历。foreach 循环会自动调用集合的 GetEnumerator() 方法,然后使用枚举器来逐个访问集合中的元素。

实现了IEnumerable接口的类是可枚举类,IEnumerable只有一个成员-GetEnumerator 方法,返回一个对象的枚举器。
在这里插入图片描述

IQuerable接口:同样获取IEnuerator枚举器

IQueryable接口继承自IEnumerable,并为其添加了查询功能。IQueryable主要用于LINQ(Language Integrated Query)查询,它允许开发者使用类似于SQL的语法来查询和操作数据。与IEnumerable不同,IQueryable的查询是可以被优化的,因为它允许查询提供程序在查询执行前分析和优化查询表达式。

IEnumerable与IQuerable接口区别

这两个接口都是用于枚举一系列的元素,但它们的工作方式和应用场景有很大的不同,主要体现在它们如何处理查询和数据访问。

IEnumerable

  • 定义IEnumerable<T>是一个在.NET Framework中用于表示可枚举对象的泛型接口。它定义了一个方法GetEnumerator(),该方法返回一个IEnumerator<T>对象,用于遍历集合中的元素。
  • 使用场景:通常用于内存中的集合,如List、Array等。
  • 扩展方法:LINQ to Objects扩展方法(如SelectWhere等)在IEnumerable<T>上操作,它们接受委托(如Func<T, TResult>)作为参数,直接在内存中的集合上执行操作。
  • 性能:因为这些操作是在内存中直接执行的,所以它们的执行速度通常很快,但可能会受到内存大小和集合大小的限制。

IQueryable

  • 定义IQueryable<T>IEnumerable<T>的扩展,用于支持查询功能。它定义了ExpressionElementType属性,以及ProviderExecute方法。IQueryable<T>通常用于支持查询提供者的集合,如数据库或XML数据源。
  • 使用场景:主要用于数据源查询,如数据库查询,允许延迟执行和查询优化。
  • 扩展方法:虽然IQueryable<T>也支持类似SelectWhere等LINQ扩展方法,但这些方法接受的是Expression<Func<T, TResult>>(或类似的表达式类型)作为参数,而不是简单的Func<T, TResult>。这些表达式被转换成表达式树(Expression Trees),然后可以由查询提供者(如LINQ to SQL或Entity Framework)转换成特定于数据源的查询语言(如SQL)。
  • 性能:因为查询被转换为针对数据源优化的形式(如SQL查询),所以这些查询可能在执行时更加高效,尤其是在处理大量数据时。然而,构建和执行这些查询可能会比直接在内存中处理数据要慢一些。

IEnumerable

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);  
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate); 

IQuerable

public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);  
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, int, bool>> predicate);  

在这两个接口的扩展方法中,一个传入的是Func委托,另一个传入的是包装了Func委托的表达式树,表达式树会对查询进行优化,而不是会像前者直接加载到内存中,在进行查询等操作。所以提升了查询效率。

执行时机与性能
IEnumerable:当遍历IEnumerable集合时,数据会被立即加载到内存中,并且查询操作在内存中进行,处理大量数据,可能会消耗大量的内存资源,此外,IEnumerable的查询操作通常不会被优化。
IQuerable:与IEnumerable不同,IQueryable的查询操作是在查询执行时才进行的,而不是在定义查询时。这使得IQueryable能够支持延迟执行(deferred execution)和查询优化。对于大型数据集或远程数据源(如数据库),这种特性可以显著提高性能。

使用场景
IEnumerable
适用于内存中的数据集合,如数组、列表等。当数据量不大,或者需要立即执行查询操作时,使用IEnumerable是合适的。
IQuerable
适用于可以优化的远程数据源,如数据库、Web服务等。当处理大量数据或需要延迟执行查询时,使用IQueryable更为高效。

总结

主要区别在于IQueryable<T>支持将LINQ查询转换为针对数据源优化的查询(如SQL查询),而IEnumerable<T>则在内存中直接操作集合。这种差异导致了在IQueryable<T>的扩展方法中使用Expression<Func<T, TResult>>而不是Func<T, TResult>,因为表达式树可以被查询提供者转换为特定于数据源的查询语言。IEnumerable适用于内存中的数据集合,而IQueryable则更适用于可以优化的远程数据源。

  • 37
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值