了解下 IEnumerable、ICollection、IList 和 IQueryable 接口

在数据集合中,经常会用 IEnumerable、ICollection、IList 和 IQueryable 这些类型来定义变量和属性,今天就来了解下这些接口的特征及适用的场景。了解这些接口特征及适用的场景,可以增强代码的强壮性且能有效的提高程序性能。先看下这几个接口的之间的关系。

图片

1、IEnumerable 接口

namespace System.Collections{    public interface IEnumerable    {        IEnumerator GetEnumerator();    }}

IEnumerable 是所有非泛型集合的基本接口。它只有一个 GetEnumerator() 方法,返回一个 IEnumerator 。IEnumerator 提供了 Current 属性、 MoveNext() 和 Reset() 方法来遍历集合的能力。也就是说,IEnumerator 提供了一个可以使用 foreach 语句遍历功能的容器。所以,如果要使用 foreach 遍历访问的对象需要实现 IEnumerable 或声明 GetEnumerator() 方法的类型。IEnumerable<T> 是安全且通用的泛型集合的基本接口。

namespace System.Collections.Generic{    public interface IEnumerable<out T> : IEnumerable    {        new IEnumerator<T> GetEnumerator();    }}

为了与非泛型集合的方法保持兼容,IEnumerable<T>实现 IEnumerable。这允许将泛型集合传递给需要 IEnumerable 对象的方法。IEnumerable<T> 相比 IEnumerable 带有更多的扩展方法,主要用于集合的过滤操作。而且,IEnumerable<T> 中的元素都是只读,类型集合的元素是不能修改。另外,在 EFCore 中使用 linq 查询并返回 IEnumerable或者IEnumerable<T> 时,它是将所有匹配到的所有对象从数据库加载到内存中,而后有其它过滤、排序等都是基于内存操作。比如:

IEnumerable<Person> query = _db.Person.Where(a => a.Name.StartsWith("苏"));query = query.Take(2);

上面这两行代码中,在数据库执行 SQL 语句会返回所有符合 Where 条件的记录,然后将这些记录装载到内存中,然后再从中取出 2 条记录。如果只是一个简单的(只读)集合,IEnumerable或者IEnumerable<T>是足够了。

2、ICollection

namespace System.Collections{    public interface ICollection : IEnumerable    {        int Count { get; }
bool IsSynchronized { get; }
object SyncRoot { get; }
void CopyTo(Array array, int index); }}

ICollection 扩展了 IEnumerable 接口,并且提供了一些属性。如:Count 返回集合中包含元素的数量,IsSynchronized 返回访问是否同步,即是否线程安全。ICollection<T> 接口:

namespace System.Collections.Generic{    public interface ICollection<T> : IEnumerable<T>, IEnumerable    {        int Count { get; }
bool IsReadOnly { get; }
void Add(T item);
bool Contains(T item);
void CopyTo(T[] array, int arrayIndex);
bool Remove(T item); }}

通过上面的接口的定义,它定义了一组方法和属性,可以根据情况来选择是否可以满足需求。ICollection<T> 与非泛型 ICollection 接口不同,ICollection<T> 接口增加了添加、删除和搜索项目这些常用的功能。当我们出现以下情况时,可以考虑使用 ICollection<T> :

  • 统计集合元素数量
  • 需要编辑集合元素
  • 搜索集合
  • 使用 IEnumerable<T> 接口中 Where 扩展方法进行过滤操作

3、IList

namespace System.Collections{    public interface IList : ICollection, IEnumerable    {        bool IsFixedSize { get; }
bool IsReadOnly { get; }
object? this[int index] { get; set; }
int Add(object? value);
void Clear();
bool Contains(object? value);
int IndexOf(object? value);
void Insert(int index, object? value);
void Remove(object? value);
void RemoveAt(int index); }}

IList 是 ICollection 接口的后代,它的实现分为三类:

  1. Read-Only :只读,无法修改集合中的元素
  2. Fixed-Size :固定集合大小,不允许添加或删除元素,但允许修改现有元素
  3. 改变集合大小 :允许添加、删除和修改元素

除了 ICollection 提供的属性和方法之外 ,还提供了通过索引来访问的集合中的元素。例如:RemoveAt(int index) 及在两个元素间插入元素 Insert(int index,object? value) 等。IList 对应的泛型列表 IList<T> 接口定义如下:

namespace System.Collections.Generic{    public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable    {        T this[int index] { get; set; }
int IndexOf(T item);
void Insert(int index, T item);
void RemoveAt(int index);
}}

如果超出 ICollection<T> 接口中提供的功能,且有以下两点中的情形,那么就可以考虑使用 IList 或 IList<T> 接口。

  • 需要修改集合中的元素
  • 需要快速定位集合中的元素或排序

4、IQueryable

namespace System.Linq{    public interface IQueryable : IEnumerable    {        Type ElementType { get; }        Expression Expression { get; }        IQueryProvider Provider { get; }    }}

根据上面的接口代码,它存在于 System.Linq 命名空间中。IQueryable 是从 IEnumerable 继承的。IEnumerable 能操作的,IQueryable 也都可以操作。IQueryable 将 Linq 表达式转换为在数据库执行的 Sql 语句;且支持自定义查询和延迟加载。IQueryable<T> 接口如下 :

namespace System.Linq{    public interface IQueryable<out T> : IEnumerable<T>, IEnumerable, IQueryable    {    }}

IQueryable 在某些情况下可以提高查询情况。例如:

IEnumerable<Person> query = _db.Person.Where(a => a.Name.StartsWith("苏"));query = query.Take(2);

IQueryable<Person> query = _db.Person.Where(a=>a.Name.StartsWith("苏"));query = query.Take(2);

两个查询看起来相同,但从性能的角度来看,IQueryable<T> 返回的是在数据库执行过滤的记录。这提高了查询和使用的内存的性能。它返回的过滤后且前2条记录,而不是全部。使用 Linq 表达式来过滤,使用 IQueryable 是最佳方案。最后,祝大家学习愉快!

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/218341.html

联系我们
联系我们
分享本页
返回顶部