虽然枚举接口IEnumerable
提供了一种向前迭代集合的协议,但是它们并没有提供确定集合大小、根据索引访问成员、搜索以及修改集合的机制。为了实现这些功能,.NET Core定义了ICollection
、IList
和IDictionary
接口。这些接口都支持泛型和非泛型版本。然而,非泛型版本的存在只是为了兼容遗留代码。
可以简单总结为:
IEnumerable<T>(和IEnumerable)
:提供了最少的功能支持(仅支持元素枚举)。ICollection<T>(和ICollection)
:提供一般的功能(例如Count属性)。IList<T>/IDictionary<K, V>
及其非泛型版本:支持最多的功能(包括根据索引/键进行“随机”访问)。
ICollection<T>和ICollection
ICollection<T>
是可以对其中的对象进行计数的标准集合接口。它可以确定集合大小(Count),确定集合中是否存在某个元素(Contains),将集合复制到一个数组(ToArray)以及确定集合是否为只读(IsReadOnly)。对于可写集合,还可以对集合元素进行添加(Add)、删除(Remove)以及清空(Clear)操作。由于它扩展自IEnumerable<T>
,因此也可以通过foreach语句进行遍历。
public interface ICollection<T> : IEnumerable<T>, IEnumerable
{
int Count { get; }
bool IsReadOnly { get; }
void Add(T item);
void Clear();
bool Contains(T item);
void CopyTo(T[] array, int arrayIndex);
bool Remove(T item);
}
非泛型的ICollection也提供了计数的功能,但是它并不支持修改或检查集合元素的功能:
public interface ICollection : IEnumerable
{
int Count { get; }
object SyncRoot { get; }
bool IsSynchronized { get; }
void CopyTo(Array array, int index);
}
IList<T>和IList
IList<T>
是按照位置对集合进行索引的标准接口。除了从ICollection<T>
和IEnumerable<T>
继承的功能之外,它还可以按位置(通过索引器)读写元素,并在特定位置插入/删除元素。
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);
}
IndexOf
方法可以对列表执行线性搜索,如果未找到指定的元素则返回-1。
IList
的非泛型版本具有更多的成员,因为(相比泛型版本)它从ICollection
继承过来的成员比较少:
public interface IList : ICollection, IEnumerable
{
object this[int index] { get; set; }
bool IsReadOnly { get; }
bool IsFixedSize { get; }
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
接口的Add
方法会返回一个整数代表最新添加元素的索引。相反,ICollection<T>
的Add
方法的返回类型为void
。
通用的List<T>
类实现了IList<T>
和IList
两种接口。C#的数组同样实现了泛型和非泛型版本的IList接口。
IReadOnlyCollection<T>与IReadOnlyList<T>
.NET Core同样定义了一系列仅提供只读操作的集合及列表接口:
public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
{
int Count { get; }
}
public interface IReadOnlyList<out T> : IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
{
T this[int index] { get; }
}
由于上述接口的类型参数仅仅在输出时使用,因此被标记为协变参数。这样我们就可以将一个“猫咪”的列表表示为一个“动物”的只读列表。
相反,在IList<T>
和ICollection<T>
中,由于T在输入输出时均被使用,因此没有标记为协变参数。