在数据集合中,经常会用 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 接口的后代,它的实现分为三类:
- Read-Only :只读,无法修改集合中的元素
- Fixed-Size :固定集合大小,不允许添加或删除元素,但允许修改现有元素
- 改变集合大小 :允许添加、删除和修改元素
除了 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